diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
index 302a19a512..d39c65ff48 100644
--- a/frontend/public/manifest.json
+++ b/frontend/public/manifest.json
@@ -1,6 +1,6 @@
{
- "short_name": "React App",
- "name": "Create React App Sample",
+ "short_name": "OpenELIS",
+ "name": "OpenELIS Global",
"icons": [
{
"src": "images/favicon-16x16.png",
diff --git a/frontend/public/service-worker.js b/frontend/public/service-worker.js
new file mode 100644
index 0000000000..1524edc672
--- /dev/null
+++ b/frontend/public/service-worker.js
@@ -0,0 +1,53 @@
+// Any other custom service worker logic can go here.
+
+self.addEventListener("push", async function (event) {
+ if (event.data) {
+ const data = event.data.json();
+ const notificationOptions = {
+ body: data.body || "Sample Body",
+ tag: data.external_id || "default-tag",
+ };
+
+ event.waitUntil(
+ self.registration.showNotification("OpenELIS Test Message Received", notificationOptions)
+ );
+ }
+});
+
+// This allows the web app to trigger skipWaiting via
+// registration.waiting.postMessage({type: 'SKIP_WAITING'})
+self.addEventListener("message", (event) => {
+ if (event.data && event.data.type === "SKIP_WAITING") {
+ self.skipWaiting();
+ }
+});
+
+
+// self.addEventListener('push', (e) => {
+// const data = e.data?.json();
+// if (data) {
+// self.registration.showNotification(data.title, {
+// body: data.body,
+// });
+// }
+// });
+
+// self.addEventListener('notificationclick', (e) => {
+// e.notification.close();
+// e.waitUntil(focusOrOpenWindow());
+// });
+
+// async function focusOrOpenWindow() {
+// const url = new URL('/', self.location.origin).href;
+
+// const allWindows = await self.clients.matchAll({
+// type: 'window',
+// });
+// const appWindow = allWindows.find((w) => w.url === url);
+
+// if (appWindow) {
+// return appWindow.focus();
+// } else {
+// return self.clients.openWindow(url);
+// }
+// }
\ No newline at end of file
diff --git a/frontend/src/components/notifications/SlideOverNotifications.jsx b/frontend/src/components/notifications/SlideOverNotifications.jsx
index b2ed3a4289..fccd9a78ad 100644
--- a/frontend/src/components/notifications/SlideOverNotifications.jsx
+++ b/frontend/src/components/notifications/SlideOverNotifications.jsx
@@ -3,9 +3,50 @@ import { formatTimestamp } from "../utils/Utils";
import Spinner from "../common/Sprinner";
import { FormattedMessage, useIntl } from "react-intl";
+
+
+
+
export default function SlideOverNotifications(props) {
const intl = useIntl();
+ async function subscribe() {
+
+
+ const public_key = "BJDIyXHWK_o9fYNwD3fUie2Ed04-yx5fxz9-GUT1c0QhfdDiGMvVbJwvB_On3XapXqIRR471uh7Snw3bfPt9niw";
+ const sw = await navigator.serviceWorker.ready;
+ const push = await sw.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: public_key,
+ });
+ const p256dh = btoa(
+ String.fromCharCode.apply(
+ null,
+ new Uint8Array(push.getKey("p256dh")),
+ ),
+ );
+ const auth = btoa(
+ String.fromCharCode.apply(
+ null,
+ new Uint8Array(push.getKey("auth")),
+ ),
+ );
+
+ const data = {
+ pf_endpoint: push.endpoint,
+ pf_p256dh: p256dh,
+ pf_auth: auth,
+ };
+
+ console.log("subsription details", data)
+
+
+
+
+
+ }
+
+
const {
loading,
notifications,
@@ -80,13 +121,13 @@ export default function SlideOverNotifications(props) {
label: intl.formatMessage({
id: "notification.slideover.button.subscribe",
}),
- onClick: () => {},
+ onClick: () => subscribe(),
},
{
icon: ,
label: intl.formatMessage({
id: "notification.slideover.button.markallasread",
- }),
+ }),
onClick: () => {
markAllNotificationsAsRead();
},
@@ -95,11 +136,11 @@ export default function SlideOverNotifications(props) {
icon: ,
label: showRead
? intl.formatMessage({
- id: "notification.slideover.button.hideread",
- })
+ id: "notification.slideover.button.hideread",
+ })
: intl.formatMessage({
- id: "notification.slideover.button.showread",
- }),
+ id: "notification.slideover.button.showread",
+ }),
onClick: () => setShowRead(!showRead),
},
].map(({ icon, label, onClick }, index) => (
diff --git a/frontend/src/index.js b/frontend/src/index.js
index 211e34a603..2027da061c 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -3,6 +3,9 @@ import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
+import * as ServiceWorker from "./serviceWorkerRegistration"
+
+ServiceWorker.registerServiceWorker()
ReactDOM.render(
diff --git a/frontend/src/serviceWorkerRegistration.js b/frontend/src/serviceWorkerRegistration.js
new file mode 100644
index 0000000000..70ce591f3f
--- /dev/null
+++ b/frontend/src/serviceWorkerRegistration.js
@@ -0,0 +1,28 @@
+// Function to register the service worker
+export function registerServiceWorker() {
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ const swUrl = `./service-worker.js`;
+
+ navigator.serviceWorker
+ .register(swUrl)
+ .then((registration) => {
+ console.log('Service Worker registered with scope:', registration.scope);
+ })
+ .catch((error) => {
+ console.error('Service Worker registration failed:', error);
+ });
+ });
+ }
+}
+
+// Function to unregister the service worker
+export function unregisterServiceWorker() {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then((registration) => {
+ registration.unregister().then((boolean) => {
+ console.log('Service Worker unregistered:', boolean);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/openelisglobal/notifications/entity/NotificationSubscription.java b/src/main/java/org/openelisglobal/notifications/entity/NotificationSubscription.java
new file mode 100644
index 0000000000..32d95115a1
--- /dev/null
+++ b/src/main/java/org/openelisglobal/notifications/entity/NotificationSubscription.java
@@ -0,0 +1,78 @@
+package org.openelisglobal.notifications.entity;
+
+import javax.persistence.*;
+import org.openelisglobal.systemuser.valueholder.SystemUser;
+
+@Entity
+@Table(name = "notification_subscriptions")
+public class NotificationSubscription {
+
+ @Id
+ @Column(name = "user_id")
+ private Long userId;
+
+ @Column(name = "pf_endpoint", nullable = false)
+ private String pfEndpoint;
+
+ @Column(name = "pf_p256dh", nullable = false)
+ private String pfP256dh;
+
+ @Column(name = "pf_auth", nullable = false)
+ private String pfAuth;
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @MapsId
+ @JoinColumn(name = "user_id", nullable = false)
+ private SystemUser user;
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public String getPfEndpoint() {
+ return pfEndpoint;
+ }
+
+ public void setPfEndpoint(String pfEndpoint) {
+ this.pfEndpoint = pfEndpoint;
+ }
+
+ public String getPfP256dh() {
+ return pfP256dh;
+ }
+
+ public void setPfP256dh(String pfP256dh) {
+ this.pfP256dh = pfP256dh;
+ }
+
+ public String getPfAuth() {
+ return pfAuth;
+ }
+
+ public void setPfAuth(String pfAuth) {
+ this.pfAuth = pfAuth;
+ }
+
+ public SystemUser getUser() {
+ return user;
+ }
+
+ public void setUser(SystemUser user) {
+ this.user = user;
+ }
+
+ @Override
+ public String toString() {
+ return "NotificationSubscription{" +
+ "userId=" + userId +
+ ", pfEndpoint='" + pfEndpoint + '\'' +
+ ", pfP256dh='" + pfP256dh + '\'' +
+ ", pfAuth='" + pfAuth + '\'' +
+ ", user=" + (user != null ? user.toString() : "null") +
+ '}';
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index ba6fc4fb5b..02b7c19f20 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -45,3 +45,9 @@ server.ssl.key-password = testtest
server.ssl.trust-store=file:/ssl/lf.truststore
server.ssl.trust-store-password=testtest
+# Push Notification config
+
+vapid.public.key=BJDIyXHWK_o9fYNwD3fUie2Ed04-yx5fxz9-GUT1c0QhfdDiGMvVbJwvB_On3XapXqIRR471uh7Snw3bfPt9niw
+vapid.private.key=FVONpka44MuWq6U8l3X4HY1hAfWM1v1IQB698gsS0KQ
+
+
diff --git a/src/main/resources/liquibase/2.8.x.x/notificationsubsriptions.xml b/src/main/resources/liquibase/2.8.x.x/notificationsubsriptions.xml
new file mode 100644
index 0000000000..4d0391eeef
--- /dev/null
+++ b/src/main/resources/liquibase/2.8.x.x/notificationsubsriptions.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+