feature/IO-3096-GlobalNotifications - Checkpoint

This commit is contained in:
Dave Richer
2025-02-25 17:23:35 -05:00
parent 08b7f0e59c
commit c5d00f7641
9 changed files with 510 additions and 566 deletions

View File

@@ -1,7 +1,7 @@
import { useSplitClient } from "@splitsoftware/splitio-react";
import { Button, Result } from "antd";
import LogRocket from "logrocket";
import React, { lazy, Suspense, useEffect, useState } from "react";
import { lazy, Suspense, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Route, Routes } from "react-router-dom";
@@ -23,8 +23,6 @@ import InstanceRenderMgr from "../utils/instanceRenderMgr";
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
import { SocketProvider } from "../contexts/SocketIO/socketContext.jsx";
import { NotificationProvider } from "../contexts/Notifications/notificationContext.jsx";
import { useSubscription, useApolloClient, gql } from "@apollo/client";
import { SUBSCRIBE_TO_NOTIFICATIONS } from "../graphql/notifications.queries.js";
const ResetPassword = lazy(() => import("../pages/reset-password/reset-password.component"));
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
@@ -48,7 +46,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
const client = useSplitClient().client;
const [listenersAdded, setListenersAdded] = useState(false);
const { t } = useTranslation();
const apolloClient = useApolloClient();
useEffect(() => {
if (!navigator.onLine) {
@@ -107,114 +104,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
}
}, [bodyshop, client, currentUser.authorized]);
// Add subscription for all unread notifications with proper normalization and format
useSubscription(SUBSCRIBE_TO_NOTIFICATIONS, {
onData: ({ data }) => {
if (data.data?.notifications) {
console.log("Subscription data received (all unread):", data.data.notifications);
const newNotifs = data.data.notifications.filter(
(newNotif) =>
!apolloClient.cache
.readQuery({
query: gql`
query GetNotifications {
notifications(order_by: { created_at: desc }) {
id
__typename
}
}
`
})
?.notifications.some((n) => n.id === newNotif.id)
);
if (newNotifs.length === 0) return;
// Use writeQuery to normalize and add new notifications as References, matching cache format
apolloClient.cache.writeQuery({
query: gql`
query GetNotifications {
notifications(order_by: { created_at: desc }) {
__typename
id
jobid
associationid
scenario_text
fcm_text
scenario_meta
created_at
read
}
}
`,
data: {
notifications: [
...newNotifs.map((notif) => ({
...notif,
__typename: "notifications",
scenario_text: JSON.stringify(
typeof notif.scenario_text === "string" ? JSON.parse(notif.scenario_text) : notif.scenario_text || []
),
scenario_meta: JSON.stringify(
typeof notif.scenario_meta === "string" ? JSON.parse(notif.scenario_meta) : notif.scenario_meta || []
)
})),
...(apolloClient.cache.readQuery({
query: gql`
query GetNotifications {
notifications(order_by: { created_at: desc }) {
__typename
id
jobid
associationid
scenario_text
fcm_text
scenario_meta
created_at
read
}
}
`
})?.notifications || [])
].sort((a, b) => new Date(b.created_at) - new Date(a.created_at)) // Sort by created_at desc
}
});
// Update notifications_aggregate for unread count
apolloClient.cache.modify({
id: "ROOT_QUERY",
fields: {
notifications_aggregate(existing = { aggregate: { count: 0 } }) {
const unreadCount = existing.aggregate.count + newNotifs.length;
console.log("Updating unread count from subscription:", unreadCount);
return {
...existing,
aggregate: {
...existing.aggregate,
count: unreadCount
}
};
}
},
optimistic: false
});
}
},
onError: (err) => {
console.error("Subscription error:", err);
// Fallback: Poll for all unread notifications if subscription fails
apolloClient
.query({
query: SUBSCRIBE_TO_NOTIFICATIONS,
variables: { where: { read: { _is_null: true } }, order_by: { created_at: "desc" } },
pollInterval: 30000
})
.catch((pollError) => console.error("Polling error:", pollError));
},
shouldResubscribe: true,
skip: !currentUser.authorized // Skip if user isnt authorized
});
if (currentUser.authorized === null) {
return <LoadingSpinner message={t("general.labels.loggingin")} />;
}