agora-appbuilder-core 4.1.9-beta.3 → 4.1.10-beta.1
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/package.json +2 -2
- package/template/Gulpfile.js +0 -16
- package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -3
- package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -2
- package/template/agora-rn-uikit/src/Reducer/index.ts +0 -2
- package/template/agora-rn-uikit/src/Rtc/Join.tsx +11 -25
- package/template/agora-rn-uikit/src/RtcConfigure.tsx +1 -14
- package/template/bridge/rtc/webNg/RtcEngine.ts +2 -2
- package/template/bridge/rtm/web/index.ts +30 -0
- package/template/customization-api/typeDefinition.ts +1 -0
- package/template/defaultConfig.js +3 -2
- package/template/global.d.ts +1 -0
- package/template/package.json +0 -2
- package/template/src/AppRoutes.tsx +3 -3
- package/template/src/ai-agent/components/ControlButtons.tsx +1 -1
- package/template/src/assets/font-styles.css +36 -0
- package/template/src/assets/fonts/icomoon.ttf +0 -0
- package/template/src/assets/selection.json +1 -1
- package/template/src/atoms/CustomIcon.tsx +8 -0
- package/template/src/atoms/Dropdown.tsx +5 -0
- package/template/src/atoms/TertiaryButton.tsx +1 -1
- package/template/src/atoms/UserAvatar.tsx +1 -1
- package/template/src/components/ChatContext.ts +5 -3
- package/template/src/components/Controls.tsx +68 -22
- package/template/src/components/DeviceConfigure.tsx +1 -1
- package/template/src/components/EventsConfigure.tsx +22 -17
- package/template/src/components/Navbar.tsx +14 -11
- package/template/src/components/RTMConfigure.tsx +31 -1036
- package/template/src/components/UserGlobalPreferenceProvider.tsx +227 -0
- package/template/src/components/beauty-effect/useBeautyEffects.tsx +50 -13
- package/template/src/components/breakout-room/BreakoutRoomPanel.tsx +58 -0
- package/template/src/components/breakout-room/context/BreakoutRoomContext.tsx +2508 -0
- package/template/src/components/breakout-room/events/BreakoutRoomEventsConfigure.tsx +272 -0
- package/template/src/components/breakout-room/events/constants.ts +17 -0
- package/template/src/components/breakout-room/hoc/BreakoutRoomNameRenderer.tsx +68 -0
- package/template/src/components/breakout-room/hooks/useBreakoutRoomExit.ts +49 -0
- package/template/src/components/breakout-room/state/reducer.ts +522 -0
- package/template/src/components/breakout-room/state/types.ts +54 -0
- package/template/src/components/breakout-room/ui/BreakoutMeetingTitle.tsx +60 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomActionMenu.tsx +136 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomAnnouncementModal.tsx +135 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomGroupSettings.tsx +588 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomMainRoomUsers.tsx +142 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomMemberActionMenu.tsx +122 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomParticipants.tsx +124 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomRaiseHand.tsx +65 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomRenameModal.tsx +227 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomSettings.tsx +140 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomTransition.tsx +52 -0
- package/template/src/components/breakout-room/ui/BreakoutRoomView.tsx +193 -0
- package/template/src/components/breakout-room/ui/ExitBreakoutRoomIconButton.tsx +79 -0
- package/template/src/components/breakout-room/ui/ParticipantManualAssignmentModal.tsx +638 -0
- package/template/src/components/breakout-room/ui/SelectParticipantAssignmentStrategy.tsx +57 -0
- package/template/src/components/chat/chatConfigure.tsx +7 -1
- package/template/src/components/chat-messages/useChatMessages.tsx +43 -11
- package/template/src/components/common/Dividers.tsx +53 -0
- package/template/src/components/controls/toolbar-items/ExitBreakoutRoomToolbarItem.tsx +13 -0
- package/template/src/components/controls/useControlPermissionMatrix.tsx +32 -4
- package/template/src/components/participants/AllHostParticipants.tsx +10 -2
- package/template/src/components/participants/Participant.tsx +7 -1
- package/template/src/components/participants/UserActionMenuOptions.tsx +12 -2
- package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +12 -8
- package/template/src/components/precall/joinWaitingRoomBtn.tsx +14 -10
- package/template/src/components/raise-hand/RaiseHandButton.tsx +50 -0
- package/template/src/components/raise-hand/RaiseHandProvider.tsx +308 -0
- package/template/src/components/raise-hand/index.ts +14 -0
- package/template/src/components/recordings/RecordingsDateTable.tsx +3 -2
- package/template/src/components/room-info/useCurrentRoomInfo.tsx +42 -0
- package/template/src/components/room-info/useSetBreakoutRoomInfo.tsx +64 -0
- package/template/src/components/useUserPreference.tsx +39 -12
- package/template/src/components/virtual-background/useVB.tsx +18 -0
- package/template/src/components/whiteboard/WhiteboardConfigure.tsx +27 -0
- package/template/src/language/default-labels/videoCallScreenLabels.ts +7 -0
- package/template/src/logger/AppBuilderLogger.tsx +11 -3
- package/template/src/pages/VideoCall.tsx +171 -518
- package/template/src/pages/video-call/BreakoutVideoCall.tsx +213 -0
- package/template/src/pages/video-call/SidePanelHeader.tsx +17 -0
- package/template/src/pages/video-call/VideoCallContent.tsx +211 -0
- package/template/src/pages/video-call/VideoCallScreen.tsx +18 -0
- package/template/src/pages/video-call/VideoCallScreenWrapper.tsx +0 -1
- package/template/src/pages/video-call/VideoCallStateWrapper.tsx +495 -0
- package/template/src/rtm/RTMConfigureBreakoutRoomProvider.tsx +882 -0
- package/template/src/rtm/RTMConfigureMainRoomProvider.tsx +757 -0
- package/template/src/rtm/RTMCoreProvider.tsx +419 -0
- package/template/src/rtm/RTMEngine.ts +188 -60
- package/template/src/rtm/RTMGlobalStateProvider.tsx +706 -0
- package/template/src/rtm/RTMStatusBanner.tsx +99 -0
- package/template/src/rtm/constants.ts +12 -0
- package/template/src/rtm/hooks/useMainRoomUserDisplayName.ts +45 -0
- package/template/src/rtm/rtm-presence-utils.ts +344 -0
- package/template/src/rtm/utils.ts +68 -1
- package/template/src/rtm-events/constants.ts +40 -1
- package/template/src/rtm-events-api/Events.ts +62 -19
- package/template/src/subComponents/ChatBubble.tsx +3 -3
- package/template/src/subComponents/ChatContainer.tsx +19 -9
- package/template/src/subComponents/LocalAudioMute.tsx +2 -2
- package/template/src/subComponents/LocalVideoMute.tsx +2 -2
- package/template/src/subComponents/SidePanelEnum.tsx +1 -0
- package/template/src/subComponents/caption/useCaption.tsx +1 -1
- package/template/src/subComponents/chat/ChatAnnouncementView.tsx +65 -0
- package/template/src/subComponents/chat/ChatSendButton.tsx +1 -0
- package/template/src/subComponents/screenshare/ScreenshareButton.tsx +16 -0
- package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +1 -1
- package/template/src/subComponents/waiting-rooms/WaitingRoomControls.tsx +7 -4
- package/template/src/utils/useDebouncedCallback.tsx +20 -0
- package/template/src/utils/useEndCall.ts +0 -2
- package/template/src/utils/useMuteToggleLocal.ts +14 -10
- package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +0 -11
- package/template/agora-rn-uikit/src/Reducer/UserBanned.ts +0 -11
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useRef,
|
|
3
|
+
useState,
|
|
4
|
+
useEffect,
|
|
5
|
+
createContext,
|
|
6
|
+
useContext,
|
|
7
|
+
useCallback,
|
|
8
|
+
useMemo,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import type {
|
|
11
|
+
RTMClient,
|
|
12
|
+
LinkStateEvent,
|
|
13
|
+
MessageEvent,
|
|
14
|
+
PresenceEvent,
|
|
15
|
+
StorageEvent,
|
|
16
|
+
Metadata,
|
|
17
|
+
SetOrUpdateUserMetadataOptions,
|
|
18
|
+
RtmLinkState,
|
|
19
|
+
} from 'agora-react-native-rtm';
|
|
20
|
+
import {UidType} from '../../agora-rn-uikit';
|
|
21
|
+
import RTMEngine from '../rtm/RTMEngine';
|
|
22
|
+
import {isWeb, isWebInternal} from '../utils/common';
|
|
23
|
+
import isSDK from '../utils/isSDK';
|
|
24
|
+
import {useAsyncEffect} from '../utils/useAsyncEffect';
|
|
25
|
+
import {nativeLinkStateMapping} from '../../bridge/rtm/web/Types';
|
|
26
|
+
import {RTMStatusBanner} from './RTMStatusBanner';
|
|
27
|
+
|
|
28
|
+
// ---- Helpers ---- //
|
|
29
|
+
const delay = (ms: number) => new Promise(r => setTimeout(r, ms));
|
|
30
|
+
|
|
31
|
+
async function loginWithBackoff(
|
|
32
|
+
rtmClient: RTMClient,
|
|
33
|
+
token: string,
|
|
34
|
+
onAttempt?: (n: number) => void,
|
|
35
|
+
maxAttempts = 5,
|
|
36
|
+
) {
|
|
37
|
+
let attempt = 0;
|
|
38
|
+
while (attempt <= maxAttempts) {
|
|
39
|
+
try {
|
|
40
|
+
try {
|
|
41
|
+
await rtmClient.logout();
|
|
42
|
+
} catch {}
|
|
43
|
+
await delay(300);
|
|
44
|
+
await rtmClient.login({token});
|
|
45
|
+
return; // success
|
|
46
|
+
} catch (e: any) {
|
|
47
|
+
attempt += 1;
|
|
48
|
+
onAttempt?.(attempt);
|
|
49
|
+
|
|
50
|
+
if (attempt > maxAttempts) {
|
|
51
|
+
const errorMsg = `RTM login failed: ${e?.message ?? e}`;
|
|
52
|
+
throw new Error(errorMsg);
|
|
53
|
+
}
|
|
54
|
+
const backoff =
|
|
55
|
+
Math.min(5000 * 2 ** (attempt - 1), 60_000) +
|
|
56
|
+
Math.floor(Math.random() * 300);
|
|
57
|
+
await delay(backoff);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---- Context ---- //
|
|
63
|
+
type MessageCallback = (message: MessageEvent) => void;
|
|
64
|
+
type PresenceCallback = (presence: PresenceEvent) => void;
|
|
65
|
+
type StorageCallback = (storage: StorageEvent) => void;
|
|
66
|
+
|
|
67
|
+
interface EventCallbacks {
|
|
68
|
+
message?: MessageCallback;
|
|
69
|
+
presence?: PresenceCallback;
|
|
70
|
+
storage?: StorageCallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface RTMContextType {
|
|
74
|
+
client: RTMClient | null;
|
|
75
|
+
connectionState: RtmLinkState;
|
|
76
|
+
error: Error | null;
|
|
77
|
+
isLoggedIn: boolean;
|
|
78
|
+
registerCallbacks: (channelName: string, callbacks: EventCallbacks) => void;
|
|
79
|
+
unregisterCallbacks: (channelName: string) => void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const RTMContext = createContext<RTMContextType>({
|
|
83
|
+
client: null,
|
|
84
|
+
connectionState: nativeLinkStateMapping.IDLE,
|
|
85
|
+
error: null,
|
|
86
|
+
isLoggedIn: false,
|
|
87
|
+
registerCallbacks: () => {},
|
|
88
|
+
unregisterCallbacks: () => {},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
interface RTMCoreProviderProps {
|
|
92
|
+
children: React.ReactNode;
|
|
93
|
+
userInfo: {
|
|
94
|
+
localUid: UidType;
|
|
95
|
+
screenShareUid: UidType;
|
|
96
|
+
isHost: boolean;
|
|
97
|
+
rtmToken?: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const RTMCoreProvider: React.FC<RTMCoreProviderProps> = ({
|
|
102
|
+
userInfo,
|
|
103
|
+
children,
|
|
104
|
+
}) => {
|
|
105
|
+
const [client, setClient] = useState<RTMClient | null>(null);
|
|
106
|
+
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
107
|
+
const [connectionState, setConnectionState] = useState(0);
|
|
108
|
+
console.log('supriya-rtm connectionState: ', connectionState);
|
|
109
|
+
const [error, setError] = useState<Error | null>(null);
|
|
110
|
+
|
|
111
|
+
const mountedRef = useRef(true);
|
|
112
|
+
const cleaningRef = useRef(false);
|
|
113
|
+
const callbackRegistry = useRef<Map<string, EventCallbacks>>(new Map());
|
|
114
|
+
const errorRef = useRef<Error | null>(null);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
mountedRef.current = true;
|
|
118
|
+
return () => {
|
|
119
|
+
mountedRef.current = false;
|
|
120
|
+
};
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
// Sync error ref with state to prevent state clearing
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
errorRef.current = error;
|
|
126
|
+
}, [error]);
|
|
127
|
+
|
|
128
|
+
// Keep error persistent if we have a failed state
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (
|
|
131
|
+
connectionState === nativeLinkStateMapping.FAILED &&
|
|
132
|
+
!error &&
|
|
133
|
+
errorRef.current
|
|
134
|
+
) {
|
|
135
|
+
setError(errorRef.current);
|
|
136
|
+
}
|
|
137
|
+
}, [connectionState, error]);
|
|
138
|
+
|
|
139
|
+
// Memoize userInfo
|
|
140
|
+
const stableUserInfo = useMemo(
|
|
141
|
+
() => ({
|
|
142
|
+
localUid: userInfo.localUid,
|
|
143
|
+
screenShareUid: userInfo.screenShareUid,
|
|
144
|
+
isHost: userInfo.isHost,
|
|
145
|
+
rtmToken: userInfo.rtmToken,
|
|
146
|
+
}),
|
|
147
|
+
[
|
|
148
|
+
userInfo.localUid,
|
|
149
|
+
userInfo.screenShareUid,
|
|
150
|
+
userInfo.isHost,
|
|
151
|
+
userInfo.rtmToken,
|
|
152
|
+
],
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const setAttribute = useCallback(async (rtmClient: RTMClient, userInfo) => {
|
|
156
|
+
const rtmAttributes = [
|
|
157
|
+
{key: 'screenUid', value: String(userInfo.screenShareUid)},
|
|
158
|
+
{key: 'isHost', value: String(userInfo.isHost)},
|
|
159
|
+
];
|
|
160
|
+
try {
|
|
161
|
+
const data: Metadata = {items: rtmAttributes};
|
|
162
|
+
const options: SetOrUpdateUserMetadataOptions = {
|
|
163
|
+
userId: `${userInfo.localUid}`,
|
|
164
|
+
};
|
|
165
|
+
// await rtmClient.storage.removeUserMetadata();
|
|
166
|
+
await rtmClient.storage.setUserMetadata(data, options);
|
|
167
|
+
} catch (setAttributeError) {
|
|
168
|
+
console.log('setAttributeError: ', setAttributeError);
|
|
169
|
+
}
|
|
170
|
+
}, []);
|
|
171
|
+
|
|
172
|
+
const registerCallbacks = useCallback(
|
|
173
|
+
(channelName: string, callbacks: EventCallbacks) => {
|
|
174
|
+
callbackRegistry.current.set(channelName, callbacks);
|
|
175
|
+
},
|
|
176
|
+
[],
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const unregisterCallbacks = useCallback((channelName: string) => {
|
|
180
|
+
callbackRegistry.current.delete(channelName);
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
// Global event listeners
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
if (!client || !userInfo?.localUid) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const handleGlobalStorageEvent = (storage: StorageEvent) => {
|
|
189
|
+
console.log(
|
|
190
|
+
'rudra-core-client ********************** ---StorageEvent event: ',
|
|
191
|
+
storage,
|
|
192
|
+
callbackRegistry,
|
|
193
|
+
);
|
|
194
|
+
// Distribute to all registered callbacks
|
|
195
|
+
callbackRegistry.current.forEach((callbacks, channelName) => {
|
|
196
|
+
try {
|
|
197
|
+
callbacks.storage?.(storage);
|
|
198
|
+
} catch (globalStorageCbError) {
|
|
199
|
+
console.log('globalStorageCbError: ', globalStorageCbError);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const handleGlobalPresenceEvent = (presence: PresenceEvent) => {
|
|
205
|
+
console.log(
|
|
206
|
+
'rudra-core-client @@@@@@@@@@@@@@@@@@@@@@@ ---PresenceEvent: ',
|
|
207
|
+
presence,
|
|
208
|
+
callbackRegistry,
|
|
209
|
+
);
|
|
210
|
+
// Distribute to all registered callbacks
|
|
211
|
+
callbackRegistry.current.forEach((callbacks, channelName) => {
|
|
212
|
+
try {
|
|
213
|
+
callbacks.presence?.(presence);
|
|
214
|
+
} catch (globalPresenceCbError) {
|
|
215
|
+
console.log('globalPresenceCbError: ', globalPresenceCbError);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const handleGlobalMessageEvent = (message: MessageEvent) => {
|
|
221
|
+
console.log(
|
|
222
|
+
'rudra-core-client ######################## ---MessageEvent event: ',
|
|
223
|
+
message,
|
|
224
|
+
);
|
|
225
|
+
if (String(userInfo.localUid) === message.publisher) {
|
|
226
|
+
console.log(
|
|
227
|
+
'rudra-core-client ######################## SKIPPING this message event as it is local',
|
|
228
|
+
message,
|
|
229
|
+
callbackRegistry,
|
|
230
|
+
);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// Distribute to all registered callbacks
|
|
234
|
+
callbackRegistry.current.forEach((callbacks, channelName) => {
|
|
235
|
+
try {
|
|
236
|
+
callbacks.message?.(message);
|
|
237
|
+
} catch (globalMessageCbError) {
|
|
238
|
+
console.log('globalMessageCbError: ', globalMessageCbError);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
client.addEventListener('storage', handleGlobalStorageEvent);
|
|
244
|
+
client.addEventListener('presence', handleGlobalPresenceEvent);
|
|
245
|
+
client.addEventListener('message', handleGlobalMessageEvent);
|
|
246
|
+
|
|
247
|
+
return () => {
|
|
248
|
+
// Remove global event listeners
|
|
249
|
+
client.removeEventListener('storage', handleGlobalStorageEvent);
|
|
250
|
+
client.removeEventListener('presence', handleGlobalPresenceEvent);
|
|
251
|
+
client.removeEventListener('message', handleGlobalMessageEvent);
|
|
252
|
+
};
|
|
253
|
+
}, [client, userInfo?.localUid]);
|
|
254
|
+
|
|
255
|
+
// Link state listener for reconnects
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (!client) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const onLink = async (evt: LinkStateEvent) => {
|
|
262
|
+
setConnectionState(evt.currentState);
|
|
263
|
+
|
|
264
|
+
if (evt.currentState === nativeLinkStateMapping.FAILED) {
|
|
265
|
+
setIsLoggedIn(false);
|
|
266
|
+
// Set error if we're in FAILED state and don't have one
|
|
267
|
+
if (!errorRef.current) {
|
|
268
|
+
const failedError = new Error('RTM connection failed');
|
|
269
|
+
errorRef.current = failedError;
|
|
270
|
+
setError(failedError);
|
|
271
|
+
}
|
|
272
|
+
} else if (evt.currentState === nativeLinkStateMapping.DISCONNECTED) {
|
|
273
|
+
setIsLoggedIn(false);
|
|
274
|
+
if (stableUserInfo.rtmToken) {
|
|
275
|
+
try {
|
|
276
|
+
await loginWithBackoff(client, stableUserInfo.rtmToken);
|
|
277
|
+
if (!mountedRef.current) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
setIsLoggedIn(true);
|
|
281
|
+
// Clear error only after successful login
|
|
282
|
+
errorRef.current = null;
|
|
283
|
+
setError(null);
|
|
284
|
+
} catch (err: any) {
|
|
285
|
+
if (!mountedRef.current) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
errorRef.current = err;
|
|
289
|
+
setError(err);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else if (evt.currentState === nativeLinkStateMapping.CONNECTED) {
|
|
293
|
+
setIsLoggedIn(true);
|
|
294
|
+
// Clear error on successful connection
|
|
295
|
+
errorRef.current = null;
|
|
296
|
+
setError(null);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
client.addEventListener('linkState', onLink);
|
|
301
|
+
return () => {
|
|
302
|
+
client.removeEventListener('linkState', onLink);
|
|
303
|
+
};
|
|
304
|
+
}, [client, stableUserInfo, setAttribute]);
|
|
305
|
+
|
|
306
|
+
// Initialize RTM
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
if (client) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
(async () => {
|
|
313
|
+
// 1, Check if engine is already connected
|
|
314
|
+
// 2. Initialize RTM Engine
|
|
315
|
+
if (!RTMEngine.getInstance()?.isEngineReady) {
|
|
316
|
+
RTMEngine.getInstance().setLocalUID(stableUserInfo.localUid);
|
|
317
|
+
}
|
|
318
|
+
const rtmClient = RTMEngine.getInstance().engine;
|
|
319
|
+
if (!rtmClient) {
|
|
320
|
+
throw new Error('Failed to create RTM client');
|
|
321
|
+
}
|
|
322
|
+
// 3. Set client after successful setup
|
|
323
|
+
setClient(rtmClient);
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
if (stableUserInfo.rtmToken) {
|
|
327
|
+
await loginWithBackoff(rtmClient, stableUserInfo.rtmToken);
|
|
328
|
+
await setAttribute(rtmClient, stableUserInfo);
|
|
329
|
+
if (!mountedRef.current) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
setIsLoggedIn(true);
|
|
333
|
+
}
|
|
334
|
+
} catch (err: any) {
|
|
335
|
+
if (!mountedRef.current) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
errorRef.current = err;
|
|
339
|
+
setError(err);
|
|
340
|
+
}
|
|
341
|
+
})();
|
|
342
|
+
}, [client, stableUserInfo, setAttribute]);
|
|
343
|
+
|
|
344
|
+
// Refresh attributes if userInfo changes while logged in
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
if (client && isLoggedIn && stableUserInfo.rtmToken) {
|
|
347
|
+
setAttribute(client, stableUserInfo).catch(console.warn);
|
|
348
|
+
}
|
|
349
|
+
}, [client, isLoggedIn, stableUserInfo, setAttribute]);
|
|
350
|
+
|
|
351
|
+
const cleanupRTM = useCallback(async () => {
|
|
352
|
+
if (cleaningRef.current) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
cleaningRef.current = true;
|
|
356
|
+
try {
|
|
357
|
+
const engine = RTMEngine.getInstance();
|
|
358
|
+
if (engine?.engine) {
|
|
359
|
+
console.log('RTM cleanup: destroying engine...');
|
|
360
|
+
await engine.destroy();
|
|
361
|
+
console.log('RTM cleanup: engine destroyed.');
|
|
362
|
+
}
|
|
363
|
+
setClient(null);
|
|
364
|
+
} catch (err) {
|
|
365
|
+
console.error('RTM cleanup failed:', err);
|
|
366
|
+
} finally {
|
|
367
|
+
cleaningRef.current = false;
|
|
368
|
+
}
|
|
369
|
+
}, []);
|
|
370
|
+
|
|
371
|
+
useAsyncEffect(() => {
|
|
372
|
+
return async () => {
|
|
373
|
+
// Cleanup
|
|
374
|
+
console.log('supriya-rtm-lifecycle cleanup');
|
|
375
|
+
await cleanupRTM();
|
|
376
|
+
};
|
|
377
|
+
}, []);
|
|
378
|
+
|
|
379
|
+
// Browser unload cleanup
|
|
380
|
+
useEffect(() => {
|
|
381
|
+
if (
|
|
382
|
+
!$config.ENABLE_CONVERSATIONAL_AI &&
|
|
383
|
+
isWebInternal() &&
|
|
384
|
+
isWeb() &&
|
|
385
|
+
!isSDK()
|
|
386
|
+
) {
|
|
387
|
+
const handleBrowserClose = (ev: BeforeUnloadEvent) => {
|
|
388
|
+
ev.preventDefault();
|
|
389
|
+
ev.returnValue = 'Are you sure you want to exit?';
|
|
390
|
+
};
|
|
391
|
+
const handleRTMCleanup = () => {
|
|
392
|
+
cleanupRTM();
|
|
393
|
+
};
|
|
394
|
+
window.addEventListener('beforeunload', handleBrowserClose);
|
|
395
|
+
window.addEventListener('pagehide', handleRTMCleanup);
|
|
396
|
+
return () => {
|
|
397
|
+
window.removeEventListener('beforeunload', handleBrowserClose);
|
|
398
|
+
window.removeEventListener('pagehide', handleRTMCleanup);
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}, [cleanupRTM]);
|
|
402
|
+
|
|
403
|
+
return (
|
|
404
|
+
<RTMContext.Provider
|
|
405
|
+
value={{
|
|
406
|
+
client,
|
|
407
|
+
isLoggedIn,
|
|
408
|
+
connectionState,
|
|
409
|
+
error,
|
|
410
|
+
registerCallbacks,
|
|
411
|
+
unregisterCallbacks,
|
|
412
|
+
}}>
|
|
413
|
+
{/* <RTMStatusBanner /> */}
|
|
414
|
+
{children}
|
|
415
|
+
</RTMContext.Provider>
|
|
416
|
+
);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
export const useRTMCore = () => useContext(RTMContext);
|