feature/IO-3096-GlobalNotifications - Checkpoint - Notification Center

This commit is contained in:
Dave Richer
2025-02-24 16:02:55 -05:00
parent 0f067fc503
commit b395839b37
9 changed files with 476 additions and 24 deletions

View File

@@ -0,0 +1,143 @@
import React, { useState, useEffect } from "react";
import { useQuery, useMutation } from "@apollo/client";
import { connect } from "react-redux";
import NotificationCenterComponent from "./notification-center.component";
import { GET_NOTIFICATIONS, MARK_ALL_NOTIFICATIONS_READ } from "../../graphql/notifications.queries";
export function NotificationCenterContainer({ visible, onClose }) {
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
const [notifications, setNotifications] = useState([]);
const [error, setError] = useState(null);
const {
data,
fetchMore,
loading,
error: queryError,
refetch
} = useQuery(GET_NOTIFICATIONS, {
variables: {
limit: 20,
offset: 0,
where: showUnreadOnly ? { read: { _is_null: true } } : {}
},
fetchPolicy: "cache-and-network",
notifyOnNetworkStatusChange: true,
onError: (err) => {
setError(err.message);
setTimeout(() => refetch(), 2000);
}
});
const [markAllReadMutation, { error: mutationError }] = useMutation(MARK_ALL_NOTIFICATIONS_READ, {
update: (cache) => {
cache.modify({
fields: {
notifications(existing = []) {
return existing.map((notif) => ({
...notif,
read: notif.read || new Date().toISOString()
}));
},
notifications_aggregate() {
return { aggregate: { count: 0 } };
}
}
});
},
onError: (err) => setError(err.message)
});
useEffect(() => {
if (data?.notifications) {
const processedNotifications = data.notifications.map((notif) => {
let scenarioText;
try {
scenarioText =
typeof notif.scenario_text === "string" ? JSON.parse(notif.scenario_text) : notif.scenario_text || [];
} catch (e) {
console.error("Error parsing JSON for notification:", notif.id, e);
scenarioText = [notif.fcm_text || "Invalid notification data"];
}
if (!Array.isArray(scenarioText)) scenarioText = [scenarioText];
return {
id: notif.id,
jobid: notif.jobid,
associationid: notif.associationid,
scenarioText,
created_at: notif.created_at,
read: notif.read,
__typename: notif.__typename
};
});
console.log("Processed Notifications:", processedNotifications);
console.log("Number of notifications to render:", processedNotifications.length);
setNotifications(processedNotifications);
setError(null);
} else {
console.log("No data yet or error in data:", data, queryError);
}
}, [data]);
useEffect(() => {
if (queryError || mutationError) {
setError(queryError?.message || mutationError?.message);
}
}, [queryError, mutationError]);
const loadMore = () => {
if (!loading && data?.notifications.length) {
fetchMore({
variables: { offset: data.notifications.length },
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return {
notifications: [...prev.notifications, ...fetchMoreResult.notifications]
};
}
}).catch((err) => setError(err.message));
}
};
const handleToggleUnreadOnly = (value) => {
setShowUnreadOnly(value);
};
const handleMarkAllRead = () => {
markAllReadMutation();
};
useEffect(() => {
if (visible) {
const virtuosoRef = { current: null };
virtuosoRef.current?.scrollTo({ top: 0 });
const handleScroll = () => {
if (virtuosoRef.current && virtuosoRef.current.scrollTop + 400 >= virtuosoRef.current.scrollHeight) {
loadMore();
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}
}, [visible, loading, data]);
console.log("Rendering NotificationCenter with notifications:", notifications.length, notifications);
return (
<NotificationCenterComponent
visible={visible}
onClose={onClose}
notifications={notifications}
loading={loading}
error={error}
showUnreadOnly={showUnreadOnly}
toggleUnreadOnly={handleToggleUnreadOnly}
markAllRead={handleMarkAllRead}
/>
);
}
export default connect(null, null)(NotificationCenterContainer);