feature: version one notification feature implemented.
This commit is contained in:
100
frontend/src/lib/realtime/RealtimeProvider.tsx
Normal file
100
frontend/src/lib/realtime/RealtimeProvider.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { io, type Socket } from 'socket.io-client';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import type { UserNotificationItem } from '@/types/notifications';
|
||||
|
||||
type RealtimeContextValue = {
|
||||
connected: boolean;
|
||||
lastNotification: UserNotificationItem | null;
|
||||
unreadCount: number | null;
|
||||
setUnreadCount: (count: number) => void;
|
||||
};
|
||||
|
||||
const RealtimeContext = createContext<RealtimeContextValue>({
|
||||
connected: false,
|
||||
lastNotification: null,
|
||||
unreadCount: null,
|
||||
setUnreadCount: () => undefined,
|
||||
});
|
||||
|
||||
function apiOrigin(): string {
|
||||
const base = process.env.NEXT_PUBLIC_API_URL ?? '';
|
||||
try {
|
||||
return new URL(base).origin;
|
||||
} catch {
|
||||
return typeof window !== 'undefined' ? window.location.origin : '';
|
||||
}
|
||||
}
|
||||
|
||||
export function RealtimeProvider({ children }: { children: ReactNode }) {
|
||||
const { user, currentOrganization, isAuthReady } = useAuth();
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [lastNotification, setLastNotification] = useState<UserNotificationItem | null>(null);
|
||||
const [unreadCount, setUnreadCount] = useState<number | null>(null);
|
||||
const socketRef = useRef<Socket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthReady || !user || !currentOrganization?.id) {
|
||||
socketRef.current?.disconnect();
|
||||
socketRef.current = null;
|
||||
setConnected(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const socket = io(`${apiOrigin()}/realtime`, {
|
||||
withCredentials: true,
|
||||
transports: ['websocket', 'polling'],
|
||||
});
|
||||
socketRef.current = socket;
|
||||
|
||||
socket.on('connect', () => setConnected(true));
|
||||
socket.on('disconnect', () => setConnected(false));
|
||||
socket.on('notification.created', (payload: { notification?: UserNotificationItem }) => {
|
||||
if (payload?.notification) {
|
||||
setLastNotification(payload.notification);
|
||||
}
|
||||
});
|
||||
socket.on('notification.unreadCount', (payload: { count?: number }) => {
|
||||
if (typeof payload?.count === 'number') {
|
||||
setUnreadCount(payload.count);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
socketRef.current = null;
|
||||
setConnected(false);
|
||||
};
|
||||
}, [isAuthReady, user, currentOrganization?.id]);
|
||||
|
||||
const setUnreadCountStable = useCallback((count: number) => {
|
||||
setUnreadCount(count);
|
||||
}, []);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
connected,
|
||||
lastNotification,
|
||||
unreadCount,
|
||||
setUnreadCount: setUnreadCountStable,
|
||||
}),
|
||||
[connected, lastNotification, unreadCount, setUnreadCountStable],
|
||||
);
|
||||
|
||||
return <RealtimeContext.Provider value={value}>{children}</RealtimeContext.Provider>;
|
||||
}
|
||||
|
||||
export function useRealtime() {
|
||||
return useContext(RealtimeContext);
|
||||
}
|
||||
Reference in New Issue
Block a user