mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-20 01:26:52 -04:00
show badge for missing license key in window title
This commit is contained in:
@@ -19,7 +19,7 @@ import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
public class LicenseChecker {
|
||||
class LicenseChecker {
|
||||
|
||||
private final JWTVerifier verifier;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ public enum FontAwesome5Icon {
|
||||
COG("\uF013"), //
|
||||
COGS("\uF085"), //
|
||||
EXCLAMATION("\uF12A"),
|
||||
EXCLAMATION_CIRCLE("\uF06A"), //
|
||||
EXCLAMATION_TRIANGLE("\uF071"), //
|
||||
EYE("\uF06E"), //
|
||||
EYE_SLASH("\uF070"), //
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user