Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .beads/issues.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
{"id":"smartfiles-u4i","title":"Apply consistent tag styling between filter and document views","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-11T21:49:21.617484+01:00","created_by":"abedurftig","updated_at":"2026-01-11T21:53:15.289719+01:00","closed_at":"2026-01-11T21:53:15.289719+01:00","close_reason":"Closed"}
{"id":"smartfiles-vap","title":"Missing Platform.runLater in ApplicationInteractor event handlers","description":"In ApplicationInteractor.java, most event handlers correctly use Platform.runLater() to update the model on the JavaFX Application Thread. However, two handlers do not:\n\n- handleDocumentTagAddedEvent (line 49-51) calls model.updateDocumentTags() directly\n- handleArchiveEntryAddedEvent (line 61-64) calls model.addDocumentFromArchiveEntry() directly\n\nSince Spring events can be published from any thread, these could cause UI updates from non-FX threads, leading to race conditions or crashes.\n\n**Fix:** Wrap these calls in Platform.runLater().","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-01-15T22:32:45.051937+01:00","created_by":"abedurftig","updated_at":"2026-01-16T20:58:09.470451+01:00","closed_at":"2026-01-16T20:58:09.470451+01:00","close_reason":"Closed"}
{"id":"smartfiles-w73","title":"Maven compiler plugin source/target mismatch","description":"In pom.xml, the properties define java.version=25 and maven.compiler.source/target=25, but the maven-compiler-plugin configuration explicitly sets source=22 and target=22 (lines 129-130), contradicting the properties.\n\n**Fix:** Remove explicit source/target from plugin config to use the property values, or update to match.","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-15T22:36:17.931659+01:00","created_by":"abedurftig","updated_at":"2026-01-16T21:17:57.858516+01:00","closed_at":"2026-01-16T21:17:57.858516+01:00","close_reason":"Closed"}
{"id":"smartfiles-we7","title":"Display document details dateLastModified and dateCreated in the document details","description":"Display the dateCreated and dateLastModified of the selected document in the document details in the bottom right.","status":"open","priority":2,"issue_type":"feature","created_at":"2026-01-07T21:31:02.161058+01:00","created_by":"abedurftig","updated_at":"2026-01-07T21:32:30.284216+01:00"}
{"id":"smartfiles-we7","title":"Display document details dateLastModified and dateCreated in the document details","description":"Display the dateCreated and dateLastModified of the selected document in the document details in the bottom right.","status":"in_progress","priority":2,"issue_type":"feature","created_at":"2026-01-07T21:31:02.161058+01:00","created_by":"abedurftig","updated_at":"2026-01-19T23:07:46.174507+01:00"}
{"id":"smartfiles-wyr","title":"Window size and position not persisted","description":"The application starts with fixed dimensions (1024x768) and does not save/restore window size or position. This is a user experience issue as preferences are lost on restart.\n\n**File:** SmartFilesApp.java (lines 22-23)\n\n**Fix:** Save window bounds to settings on close, restore on startup.","status":"open","priority":4,"issue_type":"feature","created_at":"2026-01-15T22:37:37.871391+01:00","created_by":"abedurftig","updated_at":"2026-01-15T22:37:37.871391+01:00"}
{"id":"smartfiles-yes","title":"Theme change event handler inconsistent with threading pattern","description":"handleLightThemeActivatedSettingsChangedEvent (lines 57-59) modifies the model directly without Platform.runLater(). While the theme toggle is likely called from the UI thread, the pattern should be consistent for safety.\n\n**Fix:** Wrap in Platform.runLater() for consistency with other handlers.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-15T22:36:52.962703+01:00","created_by":"abedurftig","updated_at":"2026-01-16T20:58:09.473652+01:00","closed_at":"2026-01-16T20:58:09.473652+01:00","close_reason":"Closed"}
{"id":"smartfiles-yo2","title":"APP_EXECUTOR thread pool never shutdown on exit","description":"ApplicationViewBuilder.APP_EXECUTOR is a static CachedThreadPool that is never shut down when the application exits. This can prevent clean JVM shutdown and leak threads.\n\n**File:** ApplicationViewBuilder.java (line 39)\n\n**Fix:** Register a shutdown hook or use Spring's @PreDestroy to call APP_EXECUTOR.shutdown() on application exit.","status":"open","priority":2,"issue_type":"bug","created_at":"2026-01-15T22:35:36.312096+01:00","created_by":"abedurftig","updated_at":"2026-01-15T22:35:36.312096+01:00"}
18 changes: 18 additions & 0 deletions src/main/java/dev/arne/smartfiles/app/ApplicationModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import lombok.Getter;
import lombok.Setter;

import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
import java.util.UUID;
Expand All @@ -19,7 +20,11 @@
@Setter
public class ApplicationModel {

private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("MMM d, yyyy 'at' HH:mm");

private final StringProperty selectedDocumentNameProperty = new SimpleStringProperty(null);
private final StringProperty documentDateCreatedProperty = new SimpleStringProperty("");
private final StringProperty documentDateLastModifiedProperty = new SimpleStringProperty("");
private final BooleanProperty lightModeActivated = new SimpleBooleanProperty(false);
private final ObservableList<ArchiveEntry> documentsList = FXCollections.observableArrayList();
private final FilteredList<ArchiveEntry> filteredDocuments = new FilteredList<>(documentsList, _ -> true);
Expand Down Expand Up @@ -80,12 +85,15 @@ public void setSelectedDocument(ArchiveEntry selectedDocument) {
selectedDocumentProperty.setValue(selectedDocument);
selectedDocumentNameProperty.setValue(selectedDocument.getName());
descriptionProperty.setValue(selectedDocument.getSummary());
documentDateCreatedProperty.setValue(selectedDocument.getDateCreated().format(DATE_FORMATTER));
documentDateLastModifiedProperty.setValue(selectedDocument.getDateLastModified().format(DATE_FORMATTER));
updateDocumentTags();
}

public void updateDocumentTags() {
tagsProperty.removeIf(_ -> true);
tagsProperty.addAll(selectedDocumentProperty.get().getTags());
refreshDocumentDateLastModified();
}

public void updateDescription(String description) {
Expand All @@ -94,6 +102,14 @@ public void updateDescription(String description) {
if (selectedDocument != null) {
selectedDocument.setSummary(description);
refreshDocumentInList(selectedDocument);
refreshDocumentDateLastModified();
}
}

private void refreshDocumentDateLastModified() {
var selectedDocument = selectedDocumentProperty.get();
if (selectedDocument != null) {
documentDateLastModifiedProperty.setValue(selectedDocument.getDateLastModified().format(DATE_FORMATTER));
}
}

Expand Down Expand Up @@ -136,6 +152,8 @@ public void clearSelectedDocument() {
selectedDocumentProperty.setValue(null);
selectedDocumentNameProperty.setValue(null);
descriptionProperty.setValue("");
documentDateCreatedProperty.setValue("");
documentDateLastModifiedProperty.setValue("");
tagsProperty.clear();
}
}
10 changes: 10 additions & 0 deletions src/main/java/dev/arne/smartfiles/app/ApplicationViewBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ private VBox createRightBottom() {

vBox.getChildren().add(createAreaLabel("Document details"));

var dateCreatedLabel = new Label();
dateCreatedLabel.textProperty().bind(model.getDocumentDateCreatedProperty().map(d -> d.isEmpty() ? "" : "Created: " + d));
dateCreatedLabel.getStyleClass().add("sf-footer-label");
vBox.getChildren().add(dateCreatedLabel);

var dateModifiedLabel = new Label();
dateModifiedLabel.textProperty().bind(model.getDocumentDateLastModifiedProperty().map(d -> d.isEmpty() ? "" : "Modified: " + d));
dateModifiedLabel.getStyleClass().add("sf-footer-label");
vBox.getChildren().add(dateModifiedLabel);

descriptionField = new TextField();
descriptionField.setPromptText("Click to add description");
descriptionField.textProperty().bindBidirectional(model.getDescriptionProperty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public void addTag(UUID selectedDocumentId, String text) {
var entry = archive.getArchiveEntries().get(selectedDocumentId);
var newTag = new Tag(text);
entry.getTags().add(newTag);
entry.updateLastModified();
publisher.publishEvent(new DocumentTagAddedEvent(newTag, selectedDocumentId));
publisher.publishEvent(new AllTagsUpdatedEvent(getAllUniqueTags()));
saveArchiveAndPublishUpdate();
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/dev/arne/smartfiles/app/ApplicationModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,24 @@ void clearSelectedDocument_clearsAllSelectionProperties() {
assertNull(model.getSelectedDocumentProperty().get());
assertNull(model.getSelectedDocumentNameProperty().get());
assertEquals("", model.getDescriptionProperty().get());
assertEquals("", model.getDocumentDateCreatedProperty().get());
assertEquals("", model.getDocumentDateLastModifiedProperty().get());
assertTrue(model.getTagsProperty().isEmpty());
}

@Test
void setSelectedDocument_setsDateProperties() {
var entry = createTestEntry("test.pdf", "Description");
entry.setDateCreated(LocalDateTime.of(2024, 6, 15, 14, 30));
entry.setDateLastModified(LocalDateTime.of(2024, 12, 25, 9, 0));
model.getDocumentsList().add(entry);

model.setSelectedDocument(entry);

assertEquals("Jun 15, 2024 at 14:30", model.getDocumentDateCreatedProperty().get());
assertEquals("Dec 25, 2024 at 09:00", model.getDocumentDateLastModifiedProperty().get());
}

private ArchiveEntry createTestEntry(String name, String summary) {
var entry = new ArchiveEntry();
entry.setId(UUID.randomUUID());
Expand Down