Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Reminder #2998

Merged
merged 14 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/org/cryptomator/common/settings/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class Settings {
static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01";

public final ObservableList<VaultSettings> directories;
public final BooleanProperty askedForUpdateCheck;
Expand All @@ -67,6 +68,7 @@ public class Settings {
public final StringProperty displayConfiguration;
public final StringProperty language;
public final StringProperty mountService;
public final StringProperty lastUpdateCheck;

private Consumer<Settings> saveCmd;

Expand Down Expand Up @@ -104,6 +106,7 @@ public static Settings create(Environment env) {
this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
this.language = new SimpleStringProperty(this, "language", json.language);
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);

this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());

Expand Down Expand Up @@ -131,6 +134,7 @@ public static Settings create(Environment env) {
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
lastUpdateCheck.addListener(this::somethingChanged);
}

@SuppressWarnings("deprecation")
Expand Down Expand Up @@ -185,6 +189,7 @@ SettingsJson serialized() {
json.displayConfiguration = displayConfiguration.get();
json.language = language.get();
json.mountService = mountService.get();
json.lastUpdateCheck = lastUpdateCheck.get();
return json;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ class SettingsJson {
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String preferredVolumeImpl;

@JsonProperty("lastUpdateCheck")
String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;

}
1 change: 1 addition & 0 deletions src/main/java/org/cryptomator/ui/common/FxmlFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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"), //
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public void start() {
return null;
});

appWindows.checkAndShowUpdateReminderWindow();

launchEventHandler.startHandlingLaunchEvents();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;

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})
@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})
abstract class FxApplicationModule {

private static Image createImageFromResource(String resourceName) throws IOException {
Expand Down Expand Up @@ -53,4 +54,5 @@ static PreferencesComponent providePreferencesComponent(PreferencesComponent.Bui
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
return builder.build();
}

}
19 changes: 18 additions & 1 deletion src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.unlock.UnlockWorkflow;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -45,20 +46,32 @@ public class FxApplicationWindows {
private final Lazy<PreferencesComponent> preferencesWindow;
private final QuitComponent.Builder quitWindowBuilder;
private final UnlockComponent.Factory unlockWorkflowFactory;
private final UpdateReminderComponent.Factory updateReminderWindowBuilder;
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final FilteredList<Window> visibleWindows;

@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional<TrayIntegrationProvider> trayIntegration, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, QuitComponent.Builder quitWindowBuilder, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor, VaultOptionsComponent.Factory vaultOptionsWindow) {
public FxApplicationWindows(@PrimaryStage Stage primaryStage,
Optional<TrayIntegrationProvider> trayIntegration, //
Lazy<MainWindowComponent> mainWindow, //
Lazy<PreferencesComponent> preferencesWindow, //
QuitComponent.Builder quitWindowBuilder, //
UnlockComponent.Factory unlockWorkflowFactory, //
UpdateReminderComponent.Factory updateReminderWindowBuilder, //
LockComponent.Factory lockWorkflowFactory, //
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ExecutorService executor) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.quitWindowBuilder = quitWindowBuilder;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.updateReminderWindowBuilder = updateReminderWindowBuilder;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.executor = executor;
Expand Down Expand Up @@ -117,6 +130,10 @@ public void showQuitWindow(QuitResponse response, boolean forced) {
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
}

public void checkAndShowUpdateReminderWindow() {
CompletableFuture.runAsync(() -> updateReminderWindowBuilder.create().checkAndShowUpdateReminderWindow(), Platform::runLater);
}

public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.cryptomator.ui.updatereminder;

import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;

import javafx.scene.Scene;
import javafx.stage.Stage;
import java.time.LocalDate;

@UpdateReminderScoped
@Subcomponent(modules = {UpdateReminderModule.class})
public interface UpdateReminderComponent {

@UpdateReminderWindow
Stage window();

@FxmlScene(FxmlFile.UPDATE_REMINDER)
Lazy<Scene> updateReminderScene();

Settings settings();

default void checkAndShowUpdateReminderWindow() {
if (LocalDate.parse(settings().lastUpdateCheck.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) {
Stage stage = window();
stage.setScene(updateReminderScene().get());
stage.sizeToScene();
stage.show();
}
}

@Subcomponent.Factory
interface Factory {
UpdateReminderComponent create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.cryptomator.ui.updatereminder;

import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;

import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@UpdateReminderScoped
public class UpdateReminderController implements FxController {

private final Stage window;
private final Settings settings;
private final UpdateChecker updateChecker;


@Inject
UpdateReminderController(@UpdateReminderWindow Stage window, Settings settings, UpdateChecker updateChecker) {
this.window = window;
this.settings = settings;
this.updateChecker = updateChecker;
}

@FXML
public void cancel() {
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
window.close();
}

@FXML
public void once() {
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
updateChecker.checkForUpdatesNow();
window.close();
}

@FXML
public void automatically() {
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
updateChecker.checkForUpdatesNow();
settings.checkForUpdates.set(true);
window.close();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.cryptomator.ui.updatereminder;

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 UpdateReminderModule {

@Provides
@UpdateReminderWindow
@UpdateReminderScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}

@Provides
@UpdateReminderWindow
@UpdateReminderScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("updateReminder.title"));
stage.setMinWidth(500);
stage.setMinHeight(100);
stage.initModality(Modality.APPLICATION_MODAL);
infeo marked this conversation as resolved.
Show resolved Hide resolved
return stage;
}

@Provides
@FxmlScene(FxmlFile.UPDATE_REMINDER)
@UpdateReminderScoped
static Scene provideUpdateReminderScene(@UpdateReminderWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.UPDATE_REMINDER);
}


// ------------------

@Binds
@IntoMap
@FxControllerKey(UpdateReminderController.class)
abstract FxController bindUpdateReminderController(UpdateReminderController controller);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cryptomator.ui.updatereminder;

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 UpdateReminderScoped {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.cryptomator.ui.updatereminder;

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 UpdateReminderWindow {

}
54 changes: 54 additions & 0 deletions src/main/resources/fxml/update_reminder.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<?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?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.updatereminder.UpdateReminderController"
minWidth="500"
prefWidth="500"
minHeight="145"
spacing="12"
alignment="TOP_LEFT">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="QUESTION" glyphSize="24"/>
</StackPane>
</Group>

<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%updateReminder.message" wrapText="true">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%updateReminder.description" wrapText="true"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CY" >
<buttons>
<Button text="%updateReminder.notNow" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel"/>
<Button text="%updateReminder.yesOnce" ButtonBar.buttonData="YES" onAction="#once"/>
<Button text="%updateReminder.yesAutomatically" ButtonBar.buttonData="YES" defaultButton="true" onAction="#automatically"/>
</buttons>
</ButtonBar>

</VBox>
</children>
</HBox>
10 changes: 9 additions & 1 deletion src/main/resources/i18n/strings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,12 @@ quit.lockAndQuitBtn=Lock and Quit
# Forced Quit
quit.forced.message=Some vaults could not be locked
quit.forced.description=Locking vaults was blocked by pending operations or open files. You can force lock remaining vaults, however interrupting I/O may result in the loss of unsaved data.
quit.forced.forceAndQuitBtn=Force and Quit
quit.forced.forceAndQuitBtn=Force and Quit

# Update Reminder
updateReminder.title=Update Check
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