Skip to content

Commit ddf735a

Browse files
committed
#178: improve syncing dialog while syncing. show link to heimat to control times
1 parent 66aaf9c commit ddf735a

File tree

2 files changed

+75
-36
lines changed

2 files changed

+75
-36
lines changed

src/main/java/de/doubleslash/keeptime/view/ExternalProjectsSyncController.java

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import de.doubleslash.keeptime.model.Work;
1010
import de.doubleslash.keeptime.rest.integration.heimat.model.HeimatTask;
1111
import javafx.animation.Animation;
12-
import javafx.animation.PauseTransition;
12+
import javafx.animation.KeyFrame;
1313
import javafx.animation.RotateTransition;
14+
import javafx.animation.Timeline;
1415
import javafx.application.Platform;
1516
import javafx.beans.binding.Bindings;
1617
import javafx.beans.binding.BooleanBinding;
@@ -26,9 +27,7 @@
2627
import javafx.scene.Node;
2728
import javafx.scene.control.*;
2829
import javafx.scene.effect.GaussianBlur;
29-
import javafx.scene.layout.AnchorPane;
30-
import javafx.scene.layout.HBox;
31-
import javafx.scene.layout.VBox;
30+
import javafx.scene.layout.*;
3231
import javafx.scene.paint.Color;
3332
import javafx.scene.shape.Circle;
3433
import javafx.scene.shape.SVGPath;
@@ -45,6 +44,7 @@
4544
import java.time.format.FormatStyle;
4645
import java.util.Collections;
4746
import java.util.List;
47+
import java.util.concurrent.atomic.AtomicInteger;
4848
import java.util.function.Consumer;
4949
import java.util.function.Predicate;
5050

@@ -76,6 +76,8 @@ public class ExternalProjectsSyncController {
7676

7777
@FXML
7878
private Hyperlink externalSystemLink;
79+
@FXML
80+
private Hyperlink externalSystemLinkLoadingScreen;
7981

8082
@FXML
8183
private VBox loadingScreen;
@@ -85,6 +87,10 @@ public class ExternalProjectsSyncController {
8587

8688
@FXML
8789
private Label loadingMessage;
90+
@FXML
91+
private Label loadingClosingMessage;
92+
@FXML
93+
private Region syncingIconRegion;
8894

8995
@FXML
9096
private ComboBox<HeimatTask> heimatTaskComboBox;
@@ -97,13 +103,18 @@ public class ExternalProjectsSyncController {
97103
0.1, 0.1);
98104
private final SVGPath loadingFailure = SvgNodeProvider.getSvgNodeWithScale(Resources.RESOURCE.SVG_XMARK_SOLID, 0.1,
99105
0.1);
106+
private final Color colorLoadingSpinner = Color.valueOf("#00A5E1");
107+
private final Color colorLoadingSuccess = Color.valueOf("#74a317");
108+
private final Color colorLoadingFailure = Color.valueOf("#c63329");
100109

101110
private final LocalTimeStringConverter localTimeStringConverter = new LocalTimeStringConverter(FormatStyle.MEDIUM);
102111
private ObservableList<TableRow> items;
103112

104113
private LocalDate currentReportDate;
105114
private Stage thisStage;
106115
private final HeimatController heimatController;
116+
private final RotateTransition loadingSpinnerAnimation = new RotateTransition(Duration.seconds(1),
117+
syncingIconRegion);
107118

108119
public ExternalProjectsSyncController(final HeimatController heimatController) {
109120
this.heimatController = heimatController;
@@ -158,16 +169,17 @@ public void initForDate(LocalDate currentReportDate, List<Work> currentWorkItems
158169

159170
saveButton.disableProperty().bind(projectsValidProperty);
160171
externalSystemLink.setOnAction(ae -> BrowserHelper.openURL(heimatController.getUrlForDay(currentReportDate)));
172+
externalSystemLinkLoadingScreen.setOnAction(
173+
ae -> BrowserHelper.openURL(heimatController.getUrlForDay(currentReportDate)));
161174

162175
final List<HeimatTask> tasksForDay = heimatController.getTasks(currentReportDate);
163176
final FilteredList<HeimatTask> tasksNotInList = new FilteredList<>(FXCollections.observableArrayList(tasksForDay),
164177
(task) -> items.stream().noneMatch(tr -> task.id() == tr.mapping.heimatTaskId()));
165-
items.addListener(
166-
(ListChangeListener<? super TableRow>) c -> {
167-
final Predicate<? super HeimatTask> predicate = tasksNotInList.getPredicate();
168-
tasksNotInList.setPredicate(null);
169-
tasksNotInList.setPredicate(predicate);
170-
});
178+
items.addListener((ListChangeListener<? super TableRow>) c -> {
179+
final Predicate<? super HeimatTask> predicate = tasksNotInList.getPredicate();
180+
tasksNotInList.setPredicate(null);
181+
tasksNotInList.setPredicate(predicate);
182+
});
171183
heimatTaskComboBox.setItems(tasksNotInList);
172184
addHeimatTaskButton.disableProperty()
173185
.bind(heimatTaskComboBox.getSelectionModel().selectedItemProperty().isNull());
@@ -408,7 +420,9 @@ protected List<HeimatController.HeimatErrors> call() {
408420
task.setOnSucceeded(e -> {
409421
LOG.error("Task successfull");
410422
final List<HeimatController.HeimatErrors> errors = task.getValue();
423+
int closingSeconds = 5;
411424
if (!errors.isEmpty()) {
425+
closingSeconds = 10;
412426
loadingScreenShowSyncing("Something did not work :(", loadingFailure);
413427
List<String> a = errors.stream().map(error -> {
414428
final List<Project> projects = error.mapping().mapping().projects();
@@ -423,32 +437,44 @@ protected List<HeimatController.HeimatErrors> call() {
423437

424438
showErrorDialog(a);
425439
} else {
426-
loadingScreenShowSyncing("Successfully synced!", loadingSuccess);
440+
loadingScreenShowSyncing(
441+
"Successfully synced!\nPlease always validate that everything worked like expected.",
442+
loadingSuccess);
427443
}
428444

429-
PauseTransition delay = new PauseTransition(Duration.seconds(2));
430-
// TODO maybe show countdown in UI with option to "Open day in HEIMAT"? could add 1 second again ;)
431-
delay.setOnFinished(event -> {
432-
showLoadingScreen(false);
433-
thisStage.close();
434-
});
435-
delay.play();
445+
final AtomicInteger remainingSeconds = new AtomicInteger(closingSeconds);
446+
loadingClosingMessage.setText("Closing in " + remainingSeconds + " seconds...");
447+
loadingClosingMessage.setVisible(true);
448+
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
449+
remainingSeconds.getAndDecrement();
450+
loadingClosingMessage.setText("Closing in " + remainingSeconds + " seconds...");
451+
if (remainingSeconds.get() <= 0) {
452+
showLoadingScreen(false);
453+
thisStage.close();
454+
loadingClosingMessage.setVisible(false);
455+
}
456+
}));
457+
timeline.setCycleCount(remainingSeconds.get());
458+
timeline.play();
436459
});
437460

438461
task.setOnFailed(e -> {
439462
final Throwable exception = task.getException();
440-
LOG.error("Task failed", exception);
441-
loadingScreenShowSyncing("Something did not work :(", loadingFailure);
463+
LOG.error("Task failed unexpectedly.", exception);
464+
loadingScreenShowSyncing("Something very unexpected has happened :(", loadingFailure);
442465

443-
showErrorDialog(Collections.singletonList("ERROR" + exception.getMessage()));
466+
showErrorDialog(Collections.singletonList("Error was:" + exception.getMessage()));
444467
showLoadingScreen(false);
445468
thisStage.close();
446469
});
447470
loadingScreenShowSyncing("Syncing...", loadingSpinner);
448471
Platform.runLater(() -> new Thread(task).start());
449472
});
450473

451-
cancelButton.setOnAction(ae -> thisStage.close());
474+
cancelButton.setOnAction(ae -> {
475+
showLoadingScreen(false);
476+
thisStage.close();
477+
});
452478
}
453479

454480
private static void markNodeValidOrNot(final Node textArea, final boolean isValid) {
@@ -466,33 +492,39 @@ private static boolean areSecondsOfDayValid(final long seconds) {
466492
}
467493

468494
private void initializeLoadingScreen() {
469-
loadingScreen.getChildren().add(0, loadingSpinner);
470-
471-
loadingSuccess.setFill(Color.GREEN);
495+
showLoadingScreen(false);
496+
loadingSuccess.setFill(colorLoadingSuccess);
472497
loadingSuccess.prefHeight(50);
473498
loadingSuccess.prefWidth(50);
474499

475-
loadingFailure.setFill(Color.RED);
500+
loadingFailure.setFill(colorLoadingFailure);
476501
loadingFailure.prefHeight(50);
477502
loadingFailure.prefWidth(50);
478503

504+
loadingSpinner.setFill(colorLoadingSpinner);
479505
loadingSpinner.prefHeight(50);
480506
loadingSpinner.prefWidth(50);
481507

482-
RotateTransition rotateTransition = new RotateTransition(Duration.seconds(1), loadingSpinner);
483-
rotateTransition.setByAngle(360);
484-
rotateTransition.setCycleCount(Animation.INDEFINITE);
485-
rotateTransition.play();
508+
loadingSpinnerAnimation.setNode(syncingIconRegion);
509+
loadingSpinnerAnimation.setByAngle(360);
510+
loadingSpinnerAnimation.setCycleCount(Animation.INDEFINITE);
486511
}
487512

488513
private void loadingScreenShowSyncing(String statusMessage, SVGPath icon) {
489-
final ObservableList<Node> children = loadingScreen.getChildren();
490-
children.remove(0);
491-
children.add(0, icon);
514+
if (icon == loadingSpinner) {
515+
loadingSpinnerAnimation.play();
516+
} else {
517+
loadingSpinnerAnimation.stop();
518+
}
519+
syncingIconRegion.setShape(icon);
520+
syncingIconRegion.setBackground(new Background(new BackgroundFill(icon.getFill(), null, null)));
492521
loadingMessage.setText(statusMessage);
493522
}
494523

495524
private void showLoadingScreen(final boolean show) {
525+
if (!show)
526+
loadingSpinnerAnimation.stop();
527+
loadingClosingMessage.setVisible(false);
496528
pane.setDisable(show);
497529
loadingScreen.setVisible(show);
498530
pane.setEffect(show ? new GaussianBlur() : null);

src/main/resources/layouts/externalProjectSync.fxml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<?import javafx.scene.control.TableView?>
88
<?import javafx.scene.layout.AnchorPane?>
99
<?import javafx.scene.layout.HBox?>
10+
<?import javafx.scene.layout.Region?>
1011
<?import javafx.scene.layout.StackPane?>
1112
<?import javafx.scene.layout.VBox?>
1213
<?import javafx.scene.text.Font?>
@@ -69,12 +70,18 @@
6970
</AnchorPane>
7071
<VBox fx:id="loadingScreen" alignment="CENTER" spacing="10.0" visible="false">
7172
<children>
72-
<Label fx:id="loadingMessage" text="Syncing...">
73+
<Region fx:id="syncingIconRegion" maxHeight="50.0" maxWidth="50.0" minHeight="50.0" minWidth="50.0" prefHeight="50.0" prefWidth="50.0" />
74+
<Label fx:id="loadingMessage" alignment="CENTER" contentDisplay="CENTER" text="Syncing..." textAlignment="CENTER">
7375
<font>
74-
<Font size="22.0" />
76+
<Font size="18.0" />
77+
</font>
78+
</Label>
79+
<Hyperlink fx:id="externalSystemLinkLoadingScreen" text="Open day in HEIMAT" />
80+
<Label fx:id="loadingClosingMessage" alignment="CENTER" contentDisplay="CENTER" layoutX="473.0" layoutY="307.0" text="Closing in..." textAlignment="CENTER">
81+
<font>
82+
<Font size="18.0" />
7583
</font>
7684
</Label>
77-
<Button fx:id="loadingCancelButton" mnemonicParsing="false" text="Cancel" visible="false" />
7885
</children>
7986
</VBox>
8087
</children>

0 commit comments

Comments
 (0)