Просмотр исходного кода

remove lastUpdateCheck from Settings; add lastUpdateReminder and lastSuccessfulUpdateCheck
implement updateTimeDifferenceMessage
reorder UI elements
refactor code for clarity and maintainability

Jan-Peter Klein 11 месяцев назад
Родитель
Сommit
98bcf63b2c

+ 10 - 23
src/main/java/org/cryptomator/common/settings/Settings.java

@@ -25,10 +25,6 @@ import javafx.beans.property.StringProperty;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.geometry.NodeOrientation;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
 import java.util.function.Consumer;
 
 public class Settings {
@@ -48,8 +44,8 @@ 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;
-	public static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01T10:00:00";
-
+	static final String DEFAULT_LAST_UPDATE_REMINDER = "2000-01-01";
+	public static final String DEFAULT_LAST_SUCCESSFUL_UPDATE_CHECK = "2000-01-01T00:00:00";
 	public final ObservableList<VaultSettings> directories;
 	public final BooleanProperty askedForUpdateCheck;
 	public final BooleanProperty checkForUpdates;
@@ -71,7 +67,8 @@ public class Settings {
 	public final IntegerProperty windowHeight;
 	public final StringProperty language;
 	public final StringProperty mountService;
-	public final StringProperty lastUpdateCheck;
+	public final StringProperty lastUpdateReminder;
+	public final StringProperty lastSuccessfulUpdateCheck;
 	public final StringProperty latestVersion;
 
 	private Consumer<Settings> saveCmd;
@@ -109,7 +106,8 @@ public class Settings {
 		this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
 		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.lastUpdateReminder = new SimpleStringProperty(this, "lastUpdateReminder", json.lastUpdateReminder);
+		this.lastSuccessfulUpdateCheck = new SimpleStringProperty(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
 		this.latestVersion = new SimpleStringProperty(this, "latestVersion", json.latestVersion);
 
 		this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -137,7 +135,8 @@ public class Settings {
 		windowHeight.addListener(this::somethingChanged);
 		language.addListener(this::somethingChanged);
 		mountService.addListener(this::somethingChanged);
-		lastUpdateCheck.addListener(this::somethingChanged);
+		lastUpdateReminder.addListener(this::somethingChanged);
+		lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
 		latestVersion.addListener(this::somethingChanged);
 	}
 
@@ -168,19 +167,6 @@ public class Settings {
 			});
 		}
 
-		var dateTimeString = !lastUpdateCheck.get().isEmpty() ? lastUpdateCheck.get() : DEFAULT_LAST_UPDATE_CHECK;
-		try {
-			LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME);
-			lastUpdateCheck.set(dateTime.toString());
-		} catch (DateTimeParseException e) {
-			try {
-				LocalDate date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE);
-				lastUpdateCheck.set(LocalDateTime.of(date, LocalDate.MIN.atStartOfDay().toLocalTime()).toString());
-			} catch (DateTimeParseException ex) {
-				LOG.error("The date/time format is invalid:" + dateTimeString, ex);
-			}
-		}
-
 	}
 
 	SettingsJson serialized() {
@@ -206,7 +192,8 @@ public class Settings {
 		json.windowHeight = windowHeight.get();
 		json.language = language.get();
 		json.mountService = mountService.get();
-		json.lastUpdateCheck = lastUpdateCheck.get();
+		json.lastUpdateReminder = lastUpdateReminder.get();
+		json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
 		json.latestVersion = latestVersion.get();
 		return json;
 	}

+ 5 - 2
src/main/java/org/cryptomator/common/settings/SettingsJson.java

@@ -80,8 +80,11 @@ 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;
+	@JsonProperty("lastUpdateReminder")
+	String lastUpdateReminder = Settings.DEFAULT_LAST_UPDATE_REMINDER;
+
+	@JsonProperty("lastSuccessfulUpdateCheck")
+	String lastSuccessfulUpdateCheck = Settings.DEFAULT_LAST_SUCCESSFUL_UPDATE_CHECK;
 
 	@JsonProperty("latestVersion")
 	String latestVersion;

+ 37 - 4
src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java

@@ -20,6 +20,7 @@ import javafx.concurrent.WorkerStateEvent;
 import javafx.util.Duration;
 import java.time.LocalDateTime;
 import java.util.Comparator;
+import java.util.ResourceBundle;
 
 @FxApplicationScoped
 public class UpdateChecker {
@@ -29,29 +30,36 @@ public class UpdateChecker {
 
 	private final Environment env;
 	private final Settings settings;
+	private final ResourceBundle resourceBundle;
 	private final StringProperty latestVersionProperty = new SimpleStringProperty();
 	private final ScheduledService<String> updateCheckerService;
 	private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
 	private final ObjectProperty<LocalDateTime> updateCheckTimeProperty = new SimpleObjectProperty<>();
+	private final StringProperty timeDifferenceMessageProperty = new SimpleStringProperty();
 	private final Comparator<String> versionComparator = new SemVerComparator();
 	private final BooleanBinding updateAvailable;
 
 	@Inject
 	UpdateChecker(Settings settings, //
 				  Environment env, //
+				  ResourceBundle resourceBundle, //
 				  ScheduledService<String> updateCheckerService) {
 		this.env = env;
 		this.settings = settings;
+		this.resourceBundle = resourceBundle;
 		this.updateCheckerService = updateCheckerService;
 		this.latestVersionProperty.set(settings.latestVersion.get());
-		this.updateCheckTimeProperty.set(LocalDateTime.parse(settings.lastUpdateCheck.get()));
+		this.updateCheckTimeProperty.set(LocalDateTime.parse(settings.lastSuccessfulUpdateCheck.get()));
+
 		this.updateAvailable = Bindings.createBooleanBinding(() -> {
 			var latestVersion = latestVersionProperty.get();
 			return latestVersion != null && versionComparator.compare(getCurrentVersion(), latestVersion) < 0;
 		}, latestVersionProperty);
 
+		updateTimeDifferenceMessage();
+
 		this.latestVersionProperty.addListener((_, _, newValue) -> settings.latestVersion.set(newValue));
-		this.updateCheckTimeProperty.addListener((_, _, newValue) -> settings.lastUpdateCheck.set(newValue.toString()));
+		this.updateCheckTimeProperty.addListener((_, _, newValue) -> settings.lastSuccessfulUpdateCheck.set(newValue.toString()));
 	}
 
 	public void automaticallyCheckForUpdatesIfEnabled() {
@@ -74,6 +82,27 @@ public class UpdateChecker {
 		updateCheckerService.start();
 	}
 
+	private void updateTimeDifferenceMessage() {
+		LocalDateTime updateCheckDate = updateCheckTimeProperty.get();
+		if (updateCheckDate == null || updateCheckDate.equals(LocalDateTime.parse(Settings.DEFAULT_LAST_SUCCESSFUL_UPDATE_CHECK))) {
+			timeDifferenceMessageProperty.set(resourceBundle.getString("preferences.updates.lastUpdateCheck.never"));
+			return;
+		}
+
+		var duration = java.time.Duration.between(updateCheckDate, LocalDateTime.now());
+
+		var hours = duration.toHours();
+		var days = duration.toDays();
+
+		if (hours < 1) {
+			timeDifferenceMessageProperty.set(resourceBundle.getString("preferences.updates.lastUpdateCheck.recently"));
+		} else if (hours < 24) {
+			timeDifferenceMessageProperty.set(String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.hoursAgo"), hours));
+		} else {
+			timeDifferenceMessageProperty.set(String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.daysAgo"), days));
+		}
+	}
+
 	private void checkStarted(WorkerStateEvent event) {
 		LOG.debug("Checking for updates...");
 		state.set(UpdateCheckState.IS_CHECKING);
@@ -83,13 +112,14 @@ public class UpdateChecker {
 		String latestVersion = updateCheckerService.getValue();
 		LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersion);
 		updateCheckTimeProperty.set(LocalDateTime.now());
+		updateTimeDifferenceMessage();
 		latestVersionProperty.set(latestVersion);
 		state.set(UpdateCheckState.CHECK_SUCCESSFUL);
 	}
 
 	private void checkFailed(WorkerStateEvent event) {
-		LOG.warn("Error checking for updates", event.getSource().getException());
 		state.set(UpdateCheckState.CHECK_FAILED);
+		LOG.warn("Error checking for updates", event.getSource().getException());
 	}
 
 	public enum UpdateCheckState {
@@ -108,7 +138,6 @@ public class UpdateChecker {
 		return latestVersionProperty;
 	}
 
-
 	public BooleanBinding updateAvailableProperty(){
 		return updateAvailable;
 	}
@@ -120,6 +149,10 @@ public class UpdateChecker {
 		return updateCheckTimeProperty;
 	}
 
+	public StringProperty timeDifferenceMessageProperty() {
+		return timeDifferenceMessageProperty;
+	}
+
 	public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
 		return state;
 	}

+ 15 - 6
src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java

@@ -39,6 +39,7 @@ public class UpdatesPreferencesController implements FxController {
 	private final ObjectBinding<ContentDisplay> checkForUpdatesButtonState;
 	private final ReadOnlyStringProperty latestVersion;
 	private final ObjectProperty<LocalDateTime> updateCheckDate;
+	private final ReadOnlyStringProperty timeDifferenceMessage;
 	private final String currentVersion;
 	private final BooleanBinding updateAvailable;
 	private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
@@ -58,6 +59,7 @@ public class UpdatesPreferencesController implements FxController {
 		this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
 		this.latestVersion = updateChecker.latestVersionProperty();
 		this.updateCheckDate = updateChecker.updateCheckTimeProperty();
+		this.timeDifferenceMessage = updateChecker.timeDifferenceMessageProperty();
 		this.currentVersion = updateChecker.getCurrentVersion();
 		this.updateAvailable = updateChecker.updateAvailableProperty();
 		this.updateCheckStateProperty = updateChecker.updateCheckStateProperty();
@@ -68,12 +70,11 @@ public class UpdatesPreferencesController implements FxController {
 
 		BooleanBinding isUpdateSuccessfulAndCurrent = updateCheckStateProperty.isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
 
-
 		updateCheckStateProperty.addListener((_, _, _) -> {
 			if (isUpdateSuccessfulAndCurrent.get()) {
-				upToDateLabelVisibleProperty().set(true);
+				upToDateLabelVisible.set(true);
 				PauseTransition delay = new PauseTransition(Duration.seconds(5));
-				delay.setOnFinished(_ -> upToDateLabelVisibleProperty().set(false));
+				delay.setOnFinished(_ -> upToDateLabelVisible.set(false));
 				delay.play();
 			}
 		});
@@ -122,15 +123,23 @@ public class UpdatesPreferencesController implements FxController {
 
 	public String getUpdateCheckDate() {
 		DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
-		return !updateCheckDate.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_UPDATE_CHECK)) ? updateCheckDate.get().format(formatter) : "-";
+		return !updateCheckDate.get().equals(LocalDateTime.parse(Settings.DEFAULT_LAST_SUCCESSFUL_UPDATE_CHECK)) ? updateCheckDate.get().format(formatter) : "-";
+	}
+
+	public ReadOnlyStringProperty timeDifferenceMessageProperty(){
+		return timeDifferenceMessage;
+	}
+
+	public String getTimeDifferenceMessage() {
+		return timeDifferenceMessage.get();
 	}
 
 	public BooleanProperty upToDateLabelVisibleProperty() {
 		return upToDateLabelVisible;
 	}
 
-	public final boolean isUpToDateLabelVisible() {
-		return upToDateLabelVisibleProperty().get();
+	public boolean isUpToDateLabelVisible() {
+		return upToDateLabelVisible.get();
 	}
 
 	public BooleanBinding updateAvailableProperty() {

+ 7 - 13
src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java

@@ -10,9 +10,7 @@ import org.slf4j.LoggerFactory;
 
 import javafx.scene.Scene;
 import javafx.stage.Stage;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
+import java.time.LocalDate;
 
 @UpdateReminderScoped
 @Subcomponent(modules = {UpdateReminderModule.class})
@@ -29,16 +27,12 @@ public interface UpdateReminderComponent {
 	Settings settings();
 
 	default void checkAndShowUpdateReminderWindow() {
-		try {
-			var dateTime = LocalDateTime.parse(settings().lastUpdateCheck.get(), DateTimeFormatter.ISO_DATE_TIME);
-			if (dateTime.isBefore(LocalDateTime.now().minusDays(14)) && !settings().checkForUpdates.getValue()) {
-				Stage stage = window();
-				stage.setScene(updateReminderScene().get());
-				stage.sizeToScene();
-				stage.show();
-			}
-		} catch (DateTimeParseException e) {
-			LOG.error("Failed to parse last update check time '{}':", settings().lastUpdateCheck.get(), e);
+		if (LocalDate.parse(settings().lastUpdateReminder.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) {
+			settings().lastUpdateReminder.set(LocalDate.now().toString());
+			Stage stage = window();
+			stage.setScene(updateReminderScene().get());
+			stage.sizeToScene();
+			stage.show();
 		}
 	}
 

+ 0 - 1
src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java

@@ -28,7 +28,6 @@ public class UpdateReminderController implements FxController {
 
 	@FXML
 	public void cancel() {
-		updateChecker.updateCheckTimeProperty().set(LocalDateTime.now());
 		window.close();
 	}
 

+ 6 - 2
src/main/resources/fxml/preferences_updates.fxml

@@ -11,6 +11,7 @@
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
+<?import javafx.scene.control.Tooltip?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
@@ -22,8 +23,6 @@
 		<Insets topRightBottomLeft="24"/>
 	</padding>
 	<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
-	<FormattedLabel format="%preferences.updates.latestVersion" arg1="${controller.latestVersion}" textAlignment="CENTER" wrapText="true"/>
-	<FormattedLabel format="%preferences.updates.lastUpdateCheck" arg1="${controller.updateCheckDate}" textAlignment="CENTER" wrapText="true"/>
 
 	<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
 
@@ -42,6 +41,11 @@
 			</Label>
 			<Hyperlink styleClass="hyperlink-underline" text="%preferences.general.debugDirectory" onAction="#showLogfileDirectory"/>
 		</HBox>
+		<FormattedLabel format="%preferences.updates.lastUpdateCheck" arg1="${controller.timeDifferenceMessage}" textAlignment="CENTER" wrapText="true">
+			<tooltip>
+				<Tooltip text="${controller.updateCheckDate}" showDelay="10ms"/>
+			</tooltip>
+		</FormattedLabel>
 		<Label fx:id="upToDateLabel" text="%preferences.updates.upToDate" visible="${controller.upToDateLabelVisible}" managed="${controller.upToDateLabelVisible}">
 			<graphic>
 				<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-primary" glyph="CHECK"/>

+ 5 - 2
src/main/resources/i18n/strings.properties

@@ -318,11 +318,14 @@ preferences.volume.feature.readOnly=Read-only mount
 ## Updates
 preferences.updates=Updates
 preferences.updates.currentVersion=Current Version: %s
-preferences.updates.latestVersion=Latest Version: %s
 preferences.updates.autoUpdateCheck=Check for updates automatically
 preferences.updates.checkNowBtn=Check Now
 preferences.updates.updateAvailable=Update to version %s available.
-preferences.updates.lastUpdateCheck=Last update check: %s
+preferences.updates.lastUpdateCheck=Last check: %s
+preferences.updates.lastUpdateCheck.never=never
+preferences.updates.lastUpdateCheck.recently=recently
+preferences.updates.lastUpdateCheck.daysAgo=%s days ago
+preferences.updates.lastUpdateCheck.hoursAgo=%s hours ago
 preferences.updates.checkFailed=Check failed
 preferences.updates.upToDate=Cryptomator is up-to-date.