Browse Source

show badge for missing license key in window title

Sebastian Stenzel 5 years ago
parent
commit
9b019726bb

+ 1 - 1
main/commons/src/main/java/org/cryptomator/common/LicenseChecker.java

@@ -19,7 +19,7 @@ import java.security.spec.X509EncodedKeySpec;
 import java.util.Optional;
 
 @Singleton
-public class LicenseChecker {
+class LicenseChecker {
 
 	private final JWTVerifier verifier;
 

+ 79 - 0
main/commons/src/main/java/org/cryptomator/common/LicenseHolder.java

@@ -0,0 +1,79 @@
+package org.cryptomator.common;
+
+import com.auth0.jwt.interfaces.DecodedJWT;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import org.cryptomator.common.settings.Settings;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Optional;
+
+@Singleton
+public class LicenseHolder {
+
+	private final Settings settings;
+	private final LicenseChecker licenseChecker;
+	private final ObjectProperty<DecodedJWT> validJwtClaims;
+	private final StringBinding licenseSubject;
+	private final BooleanBinding validLicenseProperty;
+
+	@Inject
+	public LicenseHolder(LicenseChecker licenseChecker, Settings settings) {
+		this.settings = settings;
+		this.licenseChecker = licenseChecker;
+		this.validJwtClaims = new SimpleObjectProperty<>();
+		this.licenseSubject = Bindings.createStringBinding(this::getLicenseSubject, validJwtClaims);
+		this.validLicenseProperty = validJwtClaims.isNotNull();
+
+		Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
+		validJwtClaims.set(claims.orElse(null));
+	}
+
+	public boolean validateAndStoreLicense(String licenseKey) {
+		Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
+		validJwtClaims.set(claims.orElse(null));
+		if (claims.isPresent()) {
+			settings.licenseKey().set(licenseKey);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/* Observable Properties */
+
+	public Optional<String> getLicenseKey() {
+		DecodedJWT claims = validJwtClaims.get();
+		if (claims != null) {
+			return Optional.of(claims.getToken());
+		} else {
+			return Optional.empty();
+		}
+	}
+
+	public StringBinding licenseSubjectProperty() {
+		return licenseSubject;
+	}
+
+	public String getLicenseSubject() {
+		DecodedJWT claims = validJwtClaims.get();
+		if (claims != null) {
+			return claims.getSubject();
+		} else {
+			return null;
+		}
+	}
+
+	public BooleanBinding validLicenseProperty() {
+		return validLicenseProperty;
+	}
+
+	public boolean isValidLicense() {
+		return validLicenseProperty.get();
+	}
+
+}

+ 1 - 0
main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java

@@ -10,6 +10,7 @@ public enum FontAwesome5Icon {
 	COG("\uF013"), //
 	COGS("\uF085"), //
 	EXCLAMATION("\uF12A"),
+	EXCLAMATION_CIRCLE("\uF06A"), //
 	EXCLAMATION_TRIANGLE("\uF071"), //
 	EYE("\uF06E"), //
 	EYE_SLASH("\uF070"), //

+ 3 - 2
main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java

@@ -18,6 +18,7 @@ import org.cryptomator.jni.MacApplicationUiState;
 import org.cryptomator.jni.MacFunctions;
 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.unlock.UnlockComponent;
 import org.slf4j.Logger;
@@ -79,9 +80,9 @@ public class FxApplication extends Application {
 		}
 	}
 
-	public void showPreferencesWindow() {
+	public void showPreferencesWindow(SelectedPreferencesTab selectedTab) {
 		Platform.runLater(() -> {
-			Stage stage = preferencesWindow.get().showPreferencesWindow();
+			Stage stage = preferencesWindow.get().showPreferencesWindow(selectedTab);
 			addVisibleStage(stage);
 			LOG.debug("Showing Preferences");
 		});

+ 15 - 2
main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java

@@ -10,10 +10,12 @@ import javafx.scene.layout.HBox;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.VBox;
 import javafx.stage.Stage;
+import org.cryptomator.common.LicenseHolder;
 import org.cryptomator.common.vaults.VaultListManager;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.fxapp.FxApplication;
 import org.cryptomator.ui.fxapp.UpdateChecker;
+import org.cryptomator.ui.preferences.SelectedPreferencesTab;
 import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,6 +40,7 @@ public class MainWindowController implements FxController {
 	private final boolean minimizeToSysTray;
 	private final UpdateChecker updateChecker;
 	private final BooleanBinding updateAvailable;
+	private final LicenseHolder licenseHolder;
 	private final VaultListManager vaultListManager;
 	private final WrongFileAlertComponent.Builder wrongFileAlert;
 	private final BooleanProperty draggingOver = new SimpleBooleanProperty();
@@ -49,12 +52,13 @@ public class MainWindowController implements FxController {
 	private double yOffset;
 
 	@Inject
-	public MainWindowController(@MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, VaultListManager vaultListManager, WrongFileAlertComponent.Builder wrongFileAlert) {
+	public MainWindowController(@MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, LicenseHolder licenseHolder, VaultListManager vaultListManager, WrongFileAlertComponent.Builder wrongFileAlert) {
 		this.window = window;
 		this.application = application;
 		this.minimizeToSysTray = minimizeToSysTray;
 		this.updateChecker = updateChecker;
 		this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
+		this.licenseHolder = licenseHolder;
 		this.vaultListManager = vaultListManager;
 		this.wrongFileAlert = wrongFileAlert;
 	}
@@ -136,11 +140,20 @@ public class MainWindowController implements FxController {
 
 	@FXML
 	public void showPreferences() {
-		application.showPreferencesWindow();
+		application.showPreferencesWindow(SelectedPreferencesTab.ANY);
+	}
+
+	@FXML
+	public void showDonationKeyPreferences() {
+		application.showPreferencesWindow(SelectedPreferencesTab.DONATION_KEY);
 	}
 
 	/* Getter/Setter */
 
+	public LicenseHolder getLicenseHolder() {
+		return licenseHolder;
+	}
+
 	public BooleanBinding updateAvailableProperty() {
 		return updateAvailable;
 	}

+ 8 - 50
main/ui/src/main/java/org/cryptomator/ui/preferences/LicenseKeyPreferencesController.java

@@ -1,60 +1,37 @@
 package org.cryptomator.ui.preferences;
 
-import com.auth0.jwt.interfaces.DecodedJWT;
 import javafx.application.Application;
-import javafx.beans.binding.Bindings;
-import javafx.beans.binding.BooleanBinding;
-import javafx.beans.binding.StringBinding;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ObservableValue;
 import javafx.fxml.FXML;
 import javafx.scene.control.TextArea;
-import org.cryptomator.common.LicenseChecker;
-import org.cryptomator.common.settings.Settings;
+import org.cryptomator.common.LicenseHolder;
 import org.cryptomator.ui.common.FxController;
 
 import javax.inject.Inject;
-import java.util.Optional;
 
 @PreferencesScoped
 public class LicenseKeyPreferencesController implements FxController {
 	
 	private static final String DONATION_URI = "https://cryptomator.org/#donate";
 
-	private final Settings settings;
 	private final Application application;
-	private final LicenseChecker licenseChecker;
-	private final ObjectProperty<DecodedJWT> validJwtClaims;
-	private final StringBinding licenseSubject;
-	private final BooleanBinding validLicenseProperty;
+	private final LicenseHolder licenseHolder;
 	public TextArea donationKeyField;
 
 	@Inject
-	LicenseKeyPreferencesController(Settings settings, Application application, LicenseChecker licenseChecker) {
-		this.settings = settings;
+	LicenseKeyPreferencesController(Application application, LicenseHolder licenseHolder) {
 		this.application = application;
-		this.licenseChecker = licenseChecker;
-		this.validJwtClaims = new SimpleObjectProperty<>();
-		this.licenseSubject = Bindings.createStringBinding(this::getLicenseSubject, validJwtClaims);
-		this.validLicenseProperty = validJwtClaims.isNotNull();
-		
-		Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
-		validJwtClaims.set(claims.orElse(null));
+		this.licenseHolder = licenseHolder;
 	}
 
 	@FXML
 	public void initialize() {
-		donationKeyField.setText(settings.licenseKey().get());
+		donationKeyField.setText(licenseHolder.getLicenseKey().orElse(null));
 		donationKeyField.textProperty().addListener(this::registrationKeyChanged);
 	}
 
 	private void registrationKeyChanged(@SuppressWarnings("unused") ObservableValue<? extends String> observable, @SuppressWarnings("unused") String oldValue, String newValue) {
-		Optional<DecodedJWT> claims = licenseChecker.check(newValue);
-		validJwtClaims.set(claims.orElse(null));
-		if (claims.isPresent()) {
-			settings.licenseKey().set(newValue);
-		}
+		licenseHolder.validateAndStoreLicense(newValue);
 	}
 
 	@FXML
@@ -62,26 +39,7 @@ public class LicenseKeyPreferencesController implements FxController {
 		application.getHostServices().showDocument(DONATION_URI);
 	}
 
-	/* Observable Properties */
-
-	public StringBinding licenseSubjectProperty() {
-		return licenseSubject;
-	}
-
-	public String getLicenseSubject() {
-		DecodedJWT claims = validJwtClaims.get();
-		if (claims != null) {
-			return claims.getSubject();
-		} else {
-			return null;
-		}
-	}
-
-	public BooleanBinding validLicenseProperty() {
-		return validLicenseProperty;
-	}
-
-	public boolean isValidLicense() {
-		return validLicenseProperty.get();
+	public LicenseHolder getLicenseHolder() {
+		return licenseHolder;
 	}
 }

+ 5 - 2
main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesComponent.java

@@ -7,9 +7,9 @@ package org.cryptomator.ui.preferences;
 
 import dagger.Lazy;
 import dagger.Subcomponent;
+import javafx.beans.property.ObjectProperty;
 import javafx.scene.Scene;
 import javafx.stage.Stage;
-import org.cryptomator.ui.common.FXMLLoaderFactory;
 import org.cryptomator.ui.common.FxmlFile;
 import org.cryptomator.ui.common.FxmlScene;
 
@@ -23,7 +23,10 @@ public interface PreferencesComponent {
 	@FxmlScene(FxmlFile.PREFERENCES)
 	Lazy<Scene> scene();
 
-	default Stage showPreferencesWindow() {
+	ObjectProperty<SelectedPreferencesTab> selectedTabProperty();
+
+	default Stage showPreferencesWindow(SelectedPreferencesTab selectedTab) {
+		selectedTabProperty().set(selectedTab);
 		Stage stage = window();
 		stage.setScene(scene().get());
 		stage.show();

+ 32 - 4
main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java

@@ -1,6 +1,9 @@
 package org.cryptomator.ui.preferences;
 
+import javafx.beans.Observable;
 import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.value.ObservableValue;
 import javafx.fxml.FXML;
 import javafx.scene.control.Tab;
 import javafx.scene.control.TabPane;
@@ -15,25 +18,50 @@ import javax.inject.Inject;
 public class PreferencesController implements FxController {
 
 	private final Stage window;
+	private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
 	private final BooleanBinding updateAvailable;
 	public TabPane tabPane;
+	public Tab generalTab;
+	public Tab volumeTab;
 	public Tab updatesTab;
+	public Tab donationKeyTab;
 
 	@Inject
-	public PreferencesController(@PreferencesWindow Stage window, UpdateChecker updateChecker) {
+	public PreferencesController(@PreferencesWindow Stage window, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, UpdateChecker updateChecker) {
 		this.window = window;
+		this.selectedTabProperty = selectedTabProperty;
 		this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
 	}
 
 	@FXML
 	public void initialize() {
 		window.setOnShowing(this::windowWillAppear);
+		selectedTabProperty.addListener(observable -> this.selectChosenTab());
 	}
 
-	private void windowWillAppear(@SuppressWarnings("unused") WindowEvent windowEvent) {
-		if (updateAvailable.get()) {
-			tabPane.getSelectionModel().select(updatesTab);
+	private void selectChosenTab() {
+		Tab toBeSelected = getTabToSelect(selectedTabProperty.get());
+		tabPane.getSelectionModel().select(toBeSelected);
+	}
+
+	private Tab getTabToSelect(SelectedPreferencesTab selectedTab) {
+		switch (selectedTab) {
+			case UPDATES:
+				return updatesTab;
+			case VOLUME:
+				return volumeTab;
+			case DONATION_KEY:
+				return donationKeyTab;
+			case GENERAL:
+				return generalTab;
+			case ANY:
+			default:
+				return updateAvailable.get() ? updatesTab : generalTab;
 		}
 	}
 
+	private void windowWillAppear(@SuppressWarnings("unused") WindowEvent windowEvent) {
+		selectChosenTab();
+	}
+
 }

+ 8 - 0
main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java

@@ -4,6 +4,8 @@ import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoMap;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
 import javafx.scene.Scene;
 import javafx.scene.image.Image;
 import javafx.stage.Stage;
@@ -23,6 +25,12 @@ import java.util.ResourceBundle;
 @Module
 abstract class PreferencesModule {
 
+	@Provides
+	@PreferencesScoped
+	static ObjectProperty<SelectedPreferencesTab> provideSelectedTabProperty() {
+		return new SimpleObjectProperty<>(SelectedPreferencesTab.ANY);
+	}
+
 	@Provides
 	@PreferencesWindow
 	@PreferencesScoped

+ 28 - 0
main/ui/src/main/java/org/cryptomator/ui/preferences/SelectedPreferencesTab.java

@@ -0,0 +1,28 @@
+package org.cryptomator.ui.preferences;
+
+public enum SelectedPreferencesTab {
+	/**
+	 * Let the controller decide which tab to show.
+	 */
+	ANY,
+
+	/**
+	 * Show general tab
+	 */
+	GENERAL,
+
+	/**
+	 * Show volume tab
+	 */
+	VOLUME,
+
+	/**
+	 * Show updates tab
+	 */
+	UPDATES,
+
+	/**
+	 * Show donation key tab
+	 */
+	DONATION_KEY,
+}

+ 2 - 1
main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java

@@ -8,6 +8,7 @@ import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultState;
 import org.cryptomator.ui.fxapp.FxApplication;
 import org.cryptomator.ui.launcher.FxApplicationStarter;
+import org.cryptomator.ui.preferences.SelectedPreferencesTab;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -150,7 +151,7 @@ class TrayMenuController {
 	}
 
 	private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
-		fxApplicationStarter.get(true).thenAccept(FxApplication::showPreferencesWindow);
+		fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
 	}
 
 	private void handleQuitRequest(EventObject e, QuitResponse response) {

+ 13 - 2
main/ui/src/main/resources/fxml/main_window.fxml

@@ -22,10 +22,21 @@
 		<children>
 			<Label text="Cryptomator"/>
 			<Region HBox.hgrow="ALWAYS"/>
+			<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#showDonationKeyPreferences" focusTraversable="false" visible="${!controller.licenseHolder.validLicense}">
+				<graphic>
+					<StackPane>
+						<FontAwesome5IconView glyph="EXCLAMATION_CIRCLE" glyphSize="16"/>
+						<Region styleClass="update-indicator" StackPane.alignment="TOP_RIGHT" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity"/>
+					</StackPane>
+				</graphic>
+				<tooltip>
+					<Tooltip text="%main.donationKeyMissing.tooltip"/>
+				</tooltip>
+			</Button>
 			<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#showPreferences" focusTraversable="false">
 				<graphic>
 					<StackPane>
-						<FontAwesome5IconView glyph="COGS"/>
+						<FontAwesome5IconView glyph="COGS" glyphSize="16"/>
 						<Region styleClass="update-indicator" visible="${controller.updateAvailable}" StackPane.alignment="TOP_RIGHT" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity"/>
 					</StackPane>
 				</graphic>
@@ -35,7 +46,7 @@
 			</Button>
 			<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#close" focusTraversable="false">
 				<graphic>
-					<FontAwesome5IconView glyph="TIMES"/>
+					<FontAwesome5IconView glyph="TIMES" glyphSize="16"/>
 				</graphic>
 				<tooltip>
 					<Tooltip text="%main.closeBtn.tooltip"/>

+ 3 - 3
main/ui/src/main/resources/fxml/preferences_donationkey.fxml

@@ -19,15 +19,15 @@
 	</padding>
 	<children>
 		<StackPane VBox.vgrow="NEVER" prefHeight="60">
-			<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.validLicense}">
+			<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.licenseHolder.validLicense}">
 				<StackPane alignment="CENTER" HBox.hgrow="NEVER">
 					<Circle styleClass="glyph-icon-primary" radius="24"/>
 					<FontAwesome5IconView styleClass="glyph-icon-white" glyph="USER_CROWN" glyphSize="24"/>
 				</StackPane>
-				<FormattedLabel format="%preferences.donationKey.registeredFor" arg1="${controller.licenseSubject}" wrapText="true"/>
+				<FormattedLabel format="%preferences.donationKey.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
 			</HBox>
 			
-			<HBox spacing="12" alignment="CENTER_LEFT" visible="${!controller.validLicense}">
+			<HBox spacing="12" alignment="CENTER_LEFT" visible="${!controller.licenseHolder.validLicense}">
 				<StackPane alignment="CENTER" HBox.hgrow="NEVER">
 					<Circle styleClass="glyph-icon-primary" radius="24"/>
 					<FontAwesome5IconView styleClass="glyph-icon-white" glyph="HAND_HOLDING_HEART" glyphSize="24"/>

+ 1 - 1
main/ui/src/main/resources/i18n/strings.properties

@@ -132,6 +132,7 @@ preferences.donationKey.getDonationKey=Get a donation key
 # Main Window
 main.closeBtn.tooltip=Close
 main.preferencesBtn.tooltip=Preferences
+main.donationKeyMissing.tooltip=Please consider donating
 ## Drag 'n' Drop
 main.dropZone.dropVault=Add this vault
 main.dropZone.unknownDragboardContent=If you want to add a vault, drag it to this window
@@ -196,4 +197,3 @@ passwordStrength.messageLabel.4=Very strong
 # Quit
 quit.prompt=Quit application? There are unlocked vaults.
 quit.lockAndQuit=Lock and Quit
-