Push notification_created socket event for instant bell badge updates
Original task: Make the bell badge update instantly when a chat
notification arrives.
Changes:
- artifacts/api-server/src/routes/conversations.ts: After
createMessageNotifications inserts the rows, emit a
`notification_created` event to each recipient's `user:{id}` room
(carrying type/relatedId/relatedType for future routing).
- artifacts/teaboy-os/src/hooks/use-notifications-socket.ts: New hook
that opens a global socket connection (when the user is logged in)
and invalidates getListNotificationsQueryKey() and
getGetHomeStatsQueryKey() whenever `notification_created` fires.
- artifacts/teaboy-os/src/App.tsx: Mounts the hook via a small
NotificationsSocketBridge component inside AuthProvider so the
listener is active on every page (home, notifications, services,
etc.), not just /chat.
Notes:
- Socket join already happens server-side on connect
(`socket.join('user:{userId}')`), so no API server topology change
was needed.
- Pre-existing TS errors in api-server and teaboy-os are unrelated to
this change.
This commit is contained in:
@@ -903,6 +903,15 @@ async function createMessageNotifications(input: {
|
||||
relatedType: "conversation",
|
||||
})),
|
||||
);
|
||||
|
||||
const { io } = await import("../index.js");
|
||||
for (const r of recipients) {
|
||||
io.to(`user:${r.userId}`).emit("notification_created", {
|
||||
type: "chat",
|
||||
relatedId: input.conversationId,
|
||||
relatedType: "conversation",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
router.post("/conversations/:id/read", requireAuth, async (req, res): Promise<void> => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { AuthProvider } from "@/contexts/AuthContext";
|
||||
import { useNotificationsSocket } from "@/hooks/use-notifications-socket";
|
||||
import NotFound from "@/pages/not-found";
|
||||
import LoginPage from "@/pages/login";
|
||||
import RegisterPage from "@/pages/register";
|
||||
@@ -23,9 +24,15 @@ const queryClient = new QueryClient({
|
||||
},
|
||||
});
|
||||
|
||||
function NotificationsSocketBridge() {
|
||||
useNotificationsSocket();
|
||||
return null;
|
||||
}
|
||||
|
||||
function Router() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<NotificationsSocketBridge />
|
||||
<Switch>
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/register" component={RegisterPage} />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useEffect } from "react";
|
||||
import { io } from "socket.io-client";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
getListNotificationsQueryKey,
|
||||
getGetHomeStatsQueryKey,
|
||||
} from "@workspace/api-client-react";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
const BASE = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
|
||||
export function useNotificationsSocket() {
|
||||
const { user } = useAuth();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
|
||||
const socket = io(window.location.origin, {
|
||||
path: `${BASE}/api/socket.io`,
|
||||
});
|
||||
|
||||
socket.on("notification_created", () => {
|
||||
queryClient.invalidateQueries({ queryKey: getListNotificationsQueryKey() });
|
||||
queryClient.invalidateQueries({ queryKey: getGetHomeStatsQueryKey() });
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [user, queryClient]);
|
||||
}
|
||||
Reference in New Issue
Block a user