Skip to content

Commit 46ce244

Browse files
authored
Merge pull request #31 from Death111/feature/#10_addNotificationPopup2
Add expired todo notifications on startup. Note field is now a textarea instead of a textfield.
2 parents add660f + 1d0293c commit 46ce244

File tree

6 files changed

+120
-32
lines changed

6 files changed

+120
-32
lines changed

src/main/java/de/doubleslash/keeptask/App.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,22 @@
2424
import de.doubleslash.keeptask.common.Resources.RESOURCE;
2525
import de.doubleslash.keeptask.controller.Controller;
2626
import de.doubleslash.keeptask.model.Model;
27+
import de.doubleslash.keeptask.model.WorkItem;
28+
import de.doubleslash.keeptask.view.EditWorkItemController;
2729
import de.doubleslash.keeptask.view.IconController;
2830
import de.doubleslash.keeptask.view.MainWindowController;
2931
import java.io.IOException;
3032
import java.io.PrintWriter;
3133
import java.io.StringWriter;
34+
import java.util.List;
35+
import java.util.Optional;
3236
import javafx.application.Application;
3337
import javafx.fxml.FXMLLoader;
3438
import javafx.scene.Scene;
3539
import javafx.scene.control.Alert;
3640
import javafx.scene.control.Alert.AlertType;
41+
import javafx.scene.control.ButtonType;
42+
import javafx.scene.control.Dialog;
3743
import javafx.scene.control.Label;
3844
import javafx.scene.control.TextArea;
3945
import javafx.scene.image.Image;
@@ -90,6 +96,7 @@ public void start(final Stage primaryStage) {
9096
try {
9197
initialiseApplication(primaryStage);
9298
LOG.info("UI successfully initialised.");
99+
showExpiredTodosDialog(primaryStage);
93100
} catch (final Exception e) {
94101
LOG.error("There was an error while initialising the UI", e);
95102
showExceptionAndExit(e, primaryStage);
@@ -130,6 +137,43 @@ private void initialiseAndShowUI(final Stage primaryStage) throws IOException {
130137
primaryStage.show();
131138
}
132139

140+
private void showExpiredTodosDialog(Stage primaryStage) {
141+
List<WorkItem> expiredWorkItems = controller.getExpiredWorkItems();
142+
for (int i = 0; i < expiredWorkItems.size(); i++) {
143+
WorkItem workItem = expiredWorkItems.get(i);
144+
final Dialog<WorkItem> dialog = new Dialog<>();
145+
dialog.setTitle("Expired Todo (" + (i + 1) + "/" + expiredWorkItems.size() + ") - "
146+
+ workItem.getProject() + " - " + workItem.getTodo());
147+
dialog.setHeaderText("This todo has expired. Please take action.");
148+
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
149+
150+
GridPane grid;
151+
final FXMLLoader loader = FxmlLayout.createLoaderFor(
152+
Resources.RESOURCE.FXML_EDIT_WORKITEM_LAYOUT);
153+
try {
154+
grid = loader.load();
155+
} catch (final IOException e) {
156+
throw new RuntimeException("Error while loading expired todo dialog layout", e);
157+
}
158+
EditWorkItemController editWorkItemController = loader.getController();
159+
editWorkItemController.initializeWith(workItem);
160+
dialog.getDialogPane().setContent(grid);
161+
dialog.initOwner(primaryStage);
162+
dialog.setResultConverter((dialogButton) -> {
163+
if (dialogButton == ButtonType.OK) {
164+
return editWorkItemController.getWorkItemFromUserInput();
165+
}
166+
return null;
167+
});
168+
169+
final Optional<WorkItem> result = dialog.showAndWait();
170+
171+
result.ifPresent(updatedWorkItem -> {
172+
controller.editWorkItem(workItem, updatedWorkItem);
173+
});
174+
}
175+
}
176+
133177
private static void showExceptionAndExit(Exception e, Stage primaryStage) {
134178
final Alert alert = new Alert(AlertType.ERROR);
135179
alert.setTitle("Error");

src/main/java/de/doubleslash/keeptask/controller/Controller.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.time.LocalDateTime;
2525
import java.util.List;
2626
import java.util.function.Predicate;
27+
import java.util.stream.Collectors;
2728
import javax.annotation.PreDestroy;
2829
import org.slf4j.Logger;
2930
import org.slf4j.LoggerFactory;
@@ -105,4 +106,12 @@ public void setFilterPredicate(Predicate<WorkItem> filterPredicate) {
105106
public void setLatestSelectedProject(String projectName) {
106107
model.setLatestSelectedProject(projectName);
107108
}
109+
110+
public List<WorkItem> getExpiredWorkItems() {
111+
return model.getWorkItems().stream()
112+
.filter(workItem -> !workItem.isFinished() && workItem.getDueDateTime() != null
113+
&& workItem.getDueDateTime().isBefore(
114+
LocalDateTime.now()))
115+
.collect(Collectors.toList());
116+
}
108117
}

src/main/java/de/doubleslash/keeptask/model/Model.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
// Copyright 2019 doubleSlash Net Business GmbH
2-
//
3-
// This file is part of KeepTime.
4-
// KeepTime is free software: you can redistribute it and/or modify
5-
// it under the terms of the GNU General Public License as published by
6-
// the Free Software Foundation, either version 3 of the License, or
7-
// (at your option) any later version.
8-
//
9-
// This program is distributed in the hope that it will be useful,
10-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
// GNU General Public License for more details.
13-
//
14-
// You should have received a copy of the GNU General Public License
15-
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1+
/*
2+
* Copyright 2023 - Death111
3+
*
4+
* This file is part of KeepTask.
5+
* KeepTask is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
1618

1719
package de.doubleslash.keeptask.model;
1820

src/main/java/de/doubleslash/keeptask/view/EditWorkItemController.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@
1919
package de.doubleslash.keeptask.view;
2020

2121
import de.doubleslash.keeptask.model.WorkItem;
22+
import java.time.LocalDate;
23+
import java.time.LocalDateTime;
24+
import java.time.temporal.ChronoUnit;
2225
import javafx.fxml.FXML;
2326
import javafx.scene.control.CheckBox;
2427
import javafx.scene.control.DatePicker;
28+
import javafx.scene.control.Label;
29+
import javafx.scene.control.TextArea;
2530
import javafx.scene.control.TextField;
31+
import javafx.scene.paint.Color;
2632
import org.slf4j.Logger;
2733
import org.slf4j.LoggerFactory;
2834
import org.springframework.stereotype.Component;
@@ -54,7 +60,10 @@ public class EditWorkItemController {
5460
private DatePicker completedDateDatePicker;
5561

5662
@FXML
57-
private TextField noteTextInput;
63+
private TextArea noteTextArea;
64+
65+
@FXML
66+
private Label dueDateStatusLabel;
5867

5968
@FXML
6069
private void initialize() {
@@ -67,6 +76,10 @@ public void initializeWith(final WorkItem workItem) {
6776
todoTextInput.setText(workItem.getTodo());
6877
if (workItem.getDueDateTime() != null) {
6978
dueDateDatePicker.setValue(workItem.getDueDateTime().toLocalDate());
79+
dueDateDatePicker.valueProperty().addListener((dp, oldValue, newValue) -> {
80+
updateDueDateStatus(newValue.atStartOfDay());
81+
});
82+
updateDueDateStatus(workItem.getDueDateTime());
7083
}
7184
priorityTextInput.setText(workItem.getPriority());
7285
projectTextInput.setText(workItem.getProject());
@@ -76,7 +89,7 @@ public void initializeWith(final WorkItem workItem) {
7689
if (workItem.getCompletedDateTime() != null) {
7790
completedDateDatePicker.setValue(workItem.getCompletedDateTime().toLocalDate());
7891
}
79-
noteTextInput.setText(workItem.getNote());
92+
noteTextArea.setText(workItem.getNote());
8093
}
8194

8295
public WorkItem getWorkItemFromUserInput() {
@@ -94,7 +107,24 @@ public WorkItem getWorkItemFromUserInput() {
94107
if (completedDateDatePicker.getValue() != null) {
95108
workItem.setCompletedDateTime(completedDateDatePicker.getValue().atStartOfDay());
96109
}
97-
workItem.setNote(noteTextInput.getText());
110+
workItem.setNote(noteTextArea.getText());
98111
return workItem;
99112
}
113+
114+
private void updateDueDateStatus(LocalDateTime dueDateTime) {
115+
LocalDate now = LocalDate.now();
116+
LocalDate dueDate = dueDateTime.toLocalDate();
117+
long daysBetween = ChronoUnit.DAYS.between(now, dueDate);
118+
119+
if (daysBetween < 0) {
120+
dueDateStatusLabel.setText(Math.abs(daysBetween) + " days expired!");
121+
dueDateStatusLabel.setTextFill(Color.DARKRED);
122+
} else if (daysBetween > 0) {
123+
dueDateStatusLabel.setText(daysBetween + " days remaining");
124+
dueDateStatusLabel.setTextFill(Color.BLACK);
125+
} else {
126+
dueDateStatusLabel.setText("Due today");
127+
dueDateStatusLabel.setTextFill(Color.ORANGE);
128+
}
129+
}
100130
}

src/main/java/de/doubleslash/keeptask/view/MainWindowController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ private Node createTodoNode(WorkItem workItem) {
252252
children1.add(createDueDateTimeLabel(dueDateTime));
253253
}
254254

255-
Label noteLabel = new Label("Note: " + workItem.getNote());
255+
Label noteLabel = new Label("Note: " + workItem.getNote().replaceAll("\r?\n", " "));
256+
noteLabel.setMaxWidth(400);
256257
if (!workItem.getNote().isEmpty()) {
257258
children1.add(noteLabel);
258259
}
@@ -343,7 +344,6 @@ private void editTodoClicked(WorkItem workItem) {
343344

344345
result.ifPresent(project -> {
345346
controller.editWorkItem(workItem, result.get());
346-
refreshTodos();
347347
});
348348
}
349349

src/main/resources/layouts/EditWorkItemDialog.fxml

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

3-
<?import javafx.geometry.Insets?>
4-
<?import javafx.scene.control.CheckBox?>
5-
<?import javafx.scene.control.DatePicker?>
6-
<?import javafx.scene.control.Label?>
7-
<?import javafx.scene.control.TextField?>
8-
<?import javafx.scene.layout.ColumnConstraints?>
9-
<?import javafx.scene.layout.GridPane?>
10-
<?import javafx.scene.layout.RowConstraints?>
3+
<?import javafx.geometry.*?>
4+
<?import javafx.scene.control.*?>
5+
<?import javafx.scene.layout.*?>
116

12-
<GridPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.doubleslash.keeptask.view.EditWorkItemController">
7+
<GridPane xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.doubleslash.keeptask.view.EditWorkItemController">
138
<columnConstraints>
149
<ColumnConstraints hgrow="SOMETIMES" maxWidth="295.0" minWidth="10.0" prefWidth="120.0" />
1510
<ColumnConstraints hgrow="SOMETIMES" maxWidth="495.0" minWidth="10.0" prefWidth="409.0" />
@@ -22,7 +17,7 @@
2217
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
2318
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
2419
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
25-
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
20+
<RowConstraints minHeight="10.0" prefHeight="100.0" vgrow="SOMETIMES" />
2621
</rowConstraints>
2722
<children>
2823
<Label text="Project" />
@@ -36,11 +31,19 @@
3631
<TextField fx:id="projectTextInput" promptText="Project" GridPane.columnIndex="1" />
3732
<TextField fx:id="priorityTextInput" promptText="Priority (High, Medium, Low)" GridPane.columnIndex="1" GridPane.rowIndex="1" />
3833
<TextField fx:id="todoTextInput" promptText="Todo" GridPane.columnIndex="1" GridPane.rowIndex="2" />
39-
<DatePicker fx:id="dueDateDatePicker" GridPane.columnIndex="1" GridPane.rowIndex="3" />
34+
<HBox alignment="CENTER_LEFT" spacing="10.0" GridPane.columnIndex="1" GridPane.rowIndex="3">
35+
<children>
36+
<DatePicker fx:id="dueDateDatePicker" />
37+
<Label fx:id="dueDateStatusLabel" contentDisplay="CENTER" />
38+
</children>
39+
<GridPane.margin>
40+
<Insets />
41+
</GridPane.margin>
42+
</HBox>
4043
<CheckBox fx:id="completedCheckBox" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="4" />
4144
<DatePicker fx:id="createdDateDatePicker" GridPane.columnIndex="1" GridPane.rowIndex="5" />
4245
<DatePicker fx:id="completedDateDatePicker" GridPane.columnIndex="1" GridPane.rowIndex="6" />
43-
<TextField fx:id="noteTextInput" promptText="Note" GridPane.columnIndex="1" GridPane.rowIndex="7" />
46+
<TextArea fx:id="noteTextArea" promptText="Note" GridPane.columnIndex="1" GridPane.rowIndex="7" />
4447
</children>
4548
<padding>
4649
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />

0 commit comments

Comments
 (0)