@djangocfg/centrifugo 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +534 -83
- package/package.json +5 -5
- package/src/components/CentrifugoMonitor/CentrifugoMonitor.tsx +137 -0
- package/src/components/CentrifugoMonitor/CentrifugoMonitorDialog.tsx +64 -0
- package/src/components/CentrifugoMonitor/CentrifugoMonitorFAB.tsx +81 -0
- package/src/components/CentrifugoMonitor/CentrifugoMonitorWidget.tsx +74 -0
- package/src/components/CentrifugoMonitor/index.ts +14 -0
- package/src/components/ConnectionStatus/ConnectionStatus.tsx +192 -0
- package/src/components/ConnectionStatus/ConnectionStatusCard.tsx +56 -0
- package/src/components/ConnectionStatus/index.ts +9 -0
- package/src/components/MessagesFeed/MessageFilters.tsx +163 -0
- package/src/components/MessagesFeed/MessagesFeed.tsx +383 -0
- package/src/components/MessagesFeed/index.ts +9 -0
- package/src/components/MessagesFeed/types.ts +31 -0
- package/src/components/SubscriptionsList/SubscriptionsList.tsx +179 -0
- package/src/components/SubscriptionsList/index.ts +7 -0
- package/src/components/index.ts +18 -0
- package/src/core/client/CentrifugoRPCClient.ts +212 -15
- package/src/core/logger/createLogger.ts +26 -3
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useRPC.ts +149 -0
- package/src/hooks/useSubscription.ts +44 -10
- package/src/index.ts +3 -4
- package/src/providers/CentrifugoProvider/CentrifugoProvider.tsx +3 -20
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* useSubscription Hook
|
|
3
3
|
*
|
|
4
4
|
* Subscribe to Centrifugo channel with auto-cleanup.
|
|
5
|
+
*
|
|
6
|
+
* Best practices:
|
|
7
|
+
* - Callbacks are stored in refs to avoid re-subscriptions
|
|
8
|
+
* - Proper error handling in callbacks (as recommended by centrifuge-js)
|
|
9
|
+
* - Cleanup on unmount
|
|
10
|
+
* - Stable unsubscribe function
|
|
5
11
|
*/
|
|
6
12
|
|
|
7
13
|
'use client';
|
|
@@ -35,21 +41,38 @@ export function useSubscription<T = any>(
|
|
|
35
41
|
const [isSubscribed, setIsSubscribed] = useState(false);
|
|
36
42
|
|
|
37
43
|
const unsubscribeRef = useRef<(() => void) | null>(null);
|
|
38
|
-
const logger = useRef(createLogger(
|
|
44
|
+
const logger = useRef(createLogger('useSubscription')).current;
|
|
45
|
+
|
|
46
|
+
// Store callbacks in refs to avoid re-subscriptions when they change
|
|
47
|
+
const onPublicationRef = useRef(onPublication);
|
|
48
|
+
const onErrorRef = useRef(onError);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
onPublicationRef.current = onPublication;
|
|
52
|
+
onErrorRef.current = onError;
|
|
53
|
+
}, [onPublication, onError]);
|
|
39
54
|
|
|
40
55
|
// Unsubscribe function
|
|
41
56
|
const unsubscribe = useCallback(() => {
|
|
42
57
|
if (unsubscribeRef.current) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
try {
|
|
59
|
+
unsubscribeRef.current();
|
|
60
|
+
unsubscribeRef.current = null;
|
|
61
|
+
setIsSubscribed(false);
|
|
62
|
+
logger.info(`Unsubscribed from channel: ${channel}`);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
logger.error(`Error during unsubscribe from ${channel}`, err);
|
|
65
|
+
}
|
|
47
66
|
}
|
|
48
67
|
}, [channel, logger]);
|
|
49
68
|
|
|
50
69
|
// Subscribe effect
|
|
51
70
|
useEffect(() => {
|
|
52
71
|
if (!client || !isConnected || !enabled) {
|
|
72
|
+
// Reset state when not connected
|
|
73
|
+
if (!isConnected && isSubscribed) {
|
|
74
|
+
setIsSubscribed(false);
|
|
75
|
+
}
|
|
53
76
|
return;
|
|
54
77
|
}
|
|
55
78
|
|
|
@@ -57,9 +80,14 @@ export function useSubscription<T = any>(
|
|
|
57
80
|
|
|
58
81
|
try {
|
|
59
82
|
const unsub = client.subscribe(channel, (receivedData: T) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
83
|
+
try {
|
|
84
|
+
// Error handling in callback as recommended by centrifuge-js docs
|
|
85
|
+
setData(receivedData);
|
|
86
|
+
setError(null);
|
|
87
|
+
onPublicationRef.current?.(receivedData);
|
|
88
|
+
} catch (callbackError) {
|
|
89
|
+
logger.error(`Error in onPublication callback for ${channel}`, callbackError);
|
|
90
|
+
}
|
|
63
91
|
});
|
|
64
92
|
|
|
65
93
|
unsubscribeRef.current = unsub;
|
|
@@ -68,7 +96,13 @@ export function useSubscription<T = any>(
|
|
|
68
96
|
} catch (err) {
|
|
69
97
|
const subscriptionError = err instanceof Error ? err : new Error('Subscription failed');
|
|
70
98
|
setError(subscriptionError);
|
|
71
|
-
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
onErrorRef.current?.(subscriptionError);
|
|
102
|
+
} catch (callbackError) {
|
|
103
|
+
logger.error(`Error in onError callback for ${channel}`, callbackError);
|
|
104
|
+
}
|
|
105
|
+
|
|
72
106
|
logger.error(`Subscription failed: ${channel}`, subscriptionError);
|
|
73
107
|
}
|
|
74
108
|
|
|
@@ -76,7 +110,7 @@ export function useSubscription<T = any>(
|
|
|
76
110
|
return () => {
|
|
77
111
|
unsubscribe();
|
|
78
112
|
};
|
|
79
|
-
}, [client, isConnected, enabled, channel,
|
|
113
|
+
}, [client, isConnected, enabled, channel, unsubscribe, logger, isSubscribed]);
|
|
80
114
|
|
|
81
115
|
return {
|
|
82
116
|
data,
|
package/src/index.ts
CHANGED
|
@@ -100,9 +100,8 @@ export { centrifugoConfig, isDevelopment, isProduction, isStaticBuild } from './
|
|
|
100
100
|
export type { CentrifugoConfig } from './config';
|
|
101
101
|
|
|
102
102
|
// ─────────────────────────────────────────────────────────────────────────
|
|
103
|
-
//
|
|
103
|
+
// Components (Universal, composable monitoring components)
|
|
104
104
|
// ─────────────────────────────────────────────────────────────────────────
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// No need to manually add <DebugPanel /> to your app
|
|
106
|
+
export * from './components';
|
|
107
|
+
export * from './debug';
|
|
@@ -7,18 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
'use client';
|
|
9
9
|
|
|
10
|
-
import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo,
|
|
10
|
+
import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo, type ReactNode } from 'react';
|
|
11
11
|
import { useAuth } from '@djangocfg/layouts';
|
|
12
12
|
import { CentrifugoRPCClient } from '../../core/client';
|
|
13
13
|
import { createLogger } from '../../core/logger';
|
|
14
14
|
import type { ConnectionState, CentrifugoToken } from '../../core/types';
|
|
15
15
|
import { LogsProvider } from '../LogsProvider';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
// Lazy load DebugPanel to reduce bundle size
|
|
19
|
-
const DebugPanel = lazy(() =>
|
|
20
|
-
import('../../debug/DebugPanel').then((m) => ({ default: m.DebugPanel }))
|
|
21
|
-
);
|
|
16
|
+
import { isStaticBuild } from '../../config';
|
|
22
17
|
|
|
23
18
|
// ─────────────────────────────────────────────────────────────────────────
|
|
24
19
|
// Context
|
|
@@ -72,7 +67,7 @@ function CentrifugoProviderInner({
|
|
|
72
67
|
url,
|
|
73
68
|
autoConnect: autoConnectProp = true,
|
|
74
69
|
}: CentrifugoProviderProps) {
|
|
75
|
-
const { isAuthenticated, isLoading, user
|
|
70
|
+
const { isAuthenticated, isLoading, user } = useAuth();
|
|
76
71
|
|
|
77
72
|
const [client, setClient] = useState<CentrifugoRPCClient | null>(null);
|
|
78
73
|
const [isConnected, setIsConnected] = useState(false);
|
|
@@ -334,21 +329,9 @@ function CentrifugoProviderInner({
|
|
|
334
329
|
enabled,
|
|
335
330
|
};
|
|
336
331
|
|
|
337
|
-
// Smart DebugPanel visibility:
|
|
338
|
-
// - Show in development mode
|
|
339
|
-
// - Show for admin users
|
|
340
|
-
const showDebugPanel = useMemo(() => {
|
|
341
|
-
return isDevelopment || isAdminUser;
|
|
342
|
-
}, [isAdminUser]);
|
|
343
|
-
|
|
344
332
|
return (
|
|
345
333
|
<CentrifugoContext.Provider value={value}>
|
|
346
334
|
{children}
|
|
347
|
-
{showDebugPanel && (
|
|
348
|
-
<Suspense fallback={null}>
|
|
349
|
-
<DebugPanel />
|
|
350
|
-
</Suspense>
|
|
351
|
-
)}
|
|
352
335
|
</CentrifugoContext.Provider>
|
|
353
336
|
);
|
|
354
337
|
}
|