userLocks = new ConcurrentHashMap<>();
public TripDetectionService(ProcessedVisitJdbcService processedVisitJdbcService,
RawLocationPointJdbcService rawLocationPointJdbcService,
TripJdbcService tripJdbcService,
- UserJdbcService userJdbcService) {
+ UserJdbcService userJdbcService,
+ UserNotificationQueueService userNotificationQueueService) {
this.processedVisitJdbcService = processedVisitJdbcService;
this.rawLocationPointJdbcService = rawLocationPointJdbcService;
this.tripJdbcService = tripJdbcService;
this.userJdbcService = userJdbcService;
+ this.userNotificationQueueService = userNotificationQueueService;
}
public void visitCreated(ProcessedVisitCreatedEvent event) {
@@ -74,7 +78,7 @@ public class TripDetectionService {
}
tripJdbcService.bulkInsert(user, trips);
-
+ userNotificationQueueService.newTrips(user, trips);
});
} finally {
userLock.unlock();
diff --git a/src/main/java/com/dedicatedcode/reitti/service/processing/VisitMergingService.java b/src/main/java/com/dedicatedcode/reitti/service/processing/VisitMergingService.java
index fd3228d1..2977f059 100644
--- a/src/main/java/com/dedicatedcode/reitti/service/processing/VisitMergingService.java
+++ b/src/main/java/com/dedicatedcode/reitti/service/processing/VisitMergingService.java
@@ -6,6 +6,7 @@ import com.dedicatedcode.reitti.event.SignificantPlaceCreatedEvent;
import com.dedicatedcode.reitti.event.VisitUpdatedEvent;
import com.dedicatedcode.reitti.model.*;
import com.dedicatedcode.reitti.repository.*;
+import com.dedicatedcode.reitti.service.UserNotificationQueueService;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
@@ -36,6 +37,7 @@ public class VisitMergingService {
private final RawLocationPointJdbcService rawLocationPointJdbcService;
private final GeometryFactory geometryFactory;
private final RabbitTemplate rabbitTemplate;
+ private final UserNotificationQueueService userNotificationQueueService;
private final long mergeThresholdSeconds;
private final long mergeThresholdMeters;
private final int searchRangeExtensionInHours;
@@ -48,6 +50,7 @@ public class VisitMergingService {
SignificantPlaceJdbcService significantPlaceJdbcService,
RawLocationPointJdbcService rawLocationPointJdbcService,
GeometryFactory geometryFactory,
+ UserNotificationQueueService userNotificationQueueService,
@Value("${reitti.visit.merge-max-stay-search-extension-days:2}") int maxStaySearchExtensionInDays,
@Value("${reitti.visit.merge-threshold-seconds:300}") long mergeThresholdSeconds,
@Value("${reitti.visit.merge-threshold-meters:100}") long mergeThresholdMeters) {
@@ -58,6 +61,7 @@ public class VisitMergingService {
this.significantPlaceJdbcService = significantPlaceJdbcService;
this.rawLocationPointJdbcService = rawLocationPointJdbcService;
this.geometryFactory = geometryFactory;
+ this.userNotificationQueueService = userNotificationQueueService;
this.mergeThresholdSeconds = mergeThresholdSeconds;
this.mergeThresholdMeters = mergeThresholdMeters;
this.searchRangeExtensionInHours = maxStaySearchExtensionInDays * 24;
@@ -119,6 +123,7 @@ public class VisitMergingService {
logger.debug("Processed [{}] visits into [{}] merged visits for user: [{}]",
allVisits.size(), processedVisits.size(), user.getUsername());
+ this.userNotificationQueueService.newVisits(user, processedVisits);
}
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index e7ec06c2..cb50da0a 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -389,3 +389,7 @@ month.9=September
month.10=October
month.11=November
month.12=December
+
+
+# SSE Events
+sse.error.connection-lost=Connection to server lost! Try reconnecting ...
diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css
index 0c67c908..2b5c05c9 100644
--- a/src/main/resources/static/css/main.css
+++ b/src/main/resources/static/css/main.css
@@ -352,6 +352,11 @@ nav {
.htmx-request .htmx-indicator {
display: inline;
+ height: initial;
+}
+
+.htmx-indicator {
+ display: none;
}
button {
@@ -1420,3 +1425,26 @@ button:disabled {
min-width: unset;
}
}
+
+#message-container {
+ text-align: center;
+ position: fixed;
+ z-index: 200;
+ width: 100%;
+ pointer-events: none;
+}
+
+#sse-message {
+ display: none;
+ background-color: var(--color-background-dark);
+ border-radius: 8px;
+ padding: 15px;
+ border: 1px solid var(--color-highlight);
+ color: var(--color-text-white);
+ box-shadow: 0 8px 25px rgba(245, 222, 179, 0.3);
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+}
+
+#sse-message.active {
+ display: inline-block;
+}
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index 466c9731..6cad016c 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -19,6 +19,9 @@
+
@@ -73,7 +76,10 @@
/*[[#{datepicker.months.oct}]]*/ 'Oct',
/*[[#{datepicker.months.nov}]]*/ 'Nov',
/*[[#{datepicker.months.dec}]]*/ 'Dec'
- ]
+ ],
+ sse: {
+ error: /*[[#{sse.error.connection-lost}]]*/ 'Connection to server lost! Will reconnect ...',
+ }
};
window.userSettings = /*[[${userSettings}]]*/ {}
@@ -474,6 +480,30 @@
}
}
+ const messagesDiv = document.getElementById('sse-message');
+ const eventSource = new EventSource('/events'); // Connect to your SSE endpoint
+
+ eventSource.onopen = function() {
+ console.log('SSE connection opened.');
+ messagesDiv.classList.remove('active')
+ };
+
+ // Listen for events with the name "message"
+ eventSource.addEventListener('message', function(event) {
+ console.log('Received message event:', event.data);
+ });
+
+
+ eventSource.onerror = function(error) {
+ console.error('EventSource failed:', error);
+ messagesDiv.innerHTML = `${window.locale.sse.error}
`;
+ messagesDiv.classList.add('active')
+ };
+
+ eventSource.onmessage = function(event) {
+ console.log('Received generic event:', event.data);
+ };
+