feature/IO-3096-GlobalNotifications - Checkpoint - clicking an individual notification will mark it read
This commit is contained in:
@@ -5,7 +5,13 @@ import { store } from "../../redux/store";
|
||||
import { addAlerts, setWssStatus } from "../../redux/application/application.actions";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
import { useNotification } from "../Notifications/notificationContext.jsx";
|
||||
import { GET_NOTIFICATIONS } from "../../graphql/notifications.queries.js";
|
||||
import {
|
||||
GET_NOTIFICATIONS,
|
||||
GET_UNREAD_COUNT,
|
||||
MARK_NOTIFICATION_READ,
|
||||
MARK_ALL_NOTIFICATIONS_READ
|
||||
} from "../../graphql/notifications.queries.js";
|
||||
import { useMutation } from "@apollo/client";
|
||||
|
||||
const SocketContext = createContext(null);
|
||||
|
||||
@@ -16,6 +22,103 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
||||
const notification = useNotification();
|
||||
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||
|
||||
const [markNotificationRead] = useMutation(MARK_NOTIFICATION_READ, {
|
||||
update: (cache, { data: { update_notifications } }) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
const updatedNotification = update_notifications.returning[0];
|
||||
|
||||
// Update the notifications list
|
||||
cache.modify({
|
||||
fields: {
|
||||
notifications(existing = [], { readField }) {
|
||||
return existing.map((notif) => {
|
||||
if (readField("id", notif) === updatedNotification.id) {
|
||||
return { ...notif, read: timestamp };
|
||||
}
|
||||
return notif;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update the unread count in notifications_aggregate
|
||||
const unreadCountQuery = cache.readQuery({
|
||||
query: GET_UNREAD_COUNT,
|
||||
variables: { associationid: userAssociationId }
|
||||
});
|
||||
|
||||
if (unreadCountQuery?.notifications_aggregate?.aggregate?.count > 0) {
|
||||
cache.writeQuery({
|
||||
query: GET_UNREAD_COUNT,
|
||||
variables: { associationid: userAssociationId },
|
||||
data: {
|
||||
notifications_aggregate: {
|
||||
...unreadCountQuery.notifications_aggregate,
|
||||
aggregate: {
|
||||
...unreadCountQuery.notifications_aggregate.aggregate,
|
||||
count: unreadCountQuery.notifications_aggregate.aggregate.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error("MARK_NOTIFICATION_READ error in SocketProvider:", err);
|
||||
}
|
||||
});
|
||||
|
||||
const [markAllNotificationsRead] = useMutation(MARK_ALL_NOTIFICATIONS_READ, {
|
||||
variables: { associationid: userAssociationId },
|
||||
update: (cache) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
cache.modify({
|
||||
fields: {
|
||||
notifications(existing = [], { readField }) {
|
||||
return existing.map((notif) => {
|
||||
if (readField("read", notif) === null && readField("associationid", notif) === userAssociationId) {
|
||||
return { ...notif, read: timestamp };
|
||||
}
|
||||
return notif;
|
||||
});
|
||||
},
|
||||
notifications_aggregate() {
|
||||
return { aggregate: { count: 0, __typename: "notifications_aggregate_fields" } };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const baseWhereClause = { associationid: { _eq: userAssociationId } };
|
||||
const cachedNotifications = cache.readQuery({
|
||||
query: GET_NOTIFICATIONS,
|
||||
variables: {
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
where: baseWhereClause
|
||||
}
|
||||
});
|
||||
|
||||
if (cachedNotifications?.notifications) {
|
||||
cache.writeQuery({
|
||||
query: GET_NOTIFICATIONS,
|
||||
variables: {
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
where: baseWhereClause
|
||||
},
|
||||
data: {
|
||||
notifications: cachedNotifications.notifications.map((notif) =>
|
||||
notif.read === null ? { ...notif, read: timestamp } : notif
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error("MARK_ALL_NOTIFICATIONS_READ error in SocketProvider:", err);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const initializeSocket = async (token) => {
|
||||
if (!bodyshop || !bodyshop.id || socketRef.current) return;
|
||||
@@ -86,8 +189,6 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
||||
const handleNotification = (data) => {
|
||||
const { jobId, jobRoNumber, notificationId, associationId, notifications } = data;
|
||||
|
||||
// Filter out notifications not matching the user's associationId
|
||||
// Technically not required.
|
||||
if (associationId !== userAssociationId) return;
|
||||
|
||||
const newNotification = {
|
||||
@@ -176,7 +277,14 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
||||
notification.info({
|
||||
message: "New Notification",
|
||||
description: (
|
||||
<ul>
|
||||
<ul
|
||||
onClick={() => {
|
||||
markNotificationRead({ variables: { id: notificationId } }).catch((e) =>
|
||||
console.error(`Error marking notification read from info: ${e?.message || ""}`)
|
||||
);
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{notifications.map((notif, index) => (
|
||||
<li key={index}>{notif.body}</li>
|
||||
))}
|
||||
@@ -237,10 +345,12 @@ export const SocketProvider = ({ children, bodyshop }) => {
|
||||
setIsConnected(false);
|
||||
}
|
||||
};
|
||||
}, [bodyshop, notification, userAssociationId]);
|
||||
}, [bodyshop, notification, userAssociationId, markNotificationRead, markAllNotificationsRead]);
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={{ socket: socketRef.current, clientId, isConnected }}>
|
||||
<SocketContext.Provider
|
||||
value={{ socket: socketRef.current, clientId, isConnected, markNotificationRead, markAllNotificationsRead }}
|
||||
>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user