Skip to content

Commit e7d8940

Browse files
committed
migration SendbirdProvider hooks
1 parent e93643c commit e7d8940

File tree

6 files changed

+419
-118
lines changed

6 files changed

+419
-118
lines changed

src/lib/Sendbird/context/SendbirdProvider.tsx

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import PUBSUB_TOPICS, { SBUGlobalPubSubTopicPayloadUnion } from '../../pubSub/to
2020
/* Hooks */
2121
import useTheme from '../../hooks/useTheme';
2222
import useMessageTemplateUtils from '../../hooks/useMessageTemplateUtils';
23-
import useConnect from '../../hooks/useConnect';
2423
import { useUnmount } from '../../../hooks/useUnmount';
25-
import { disconnectSdk } from '../../hooks/useConnect/disconnectSdk';
2624
import useHTMLTextDirection from '../../../hooks/useHTMLTextDirection';
2725
import useOnlineStatus from '../../hooks/useOnlineStatus';
2826
import { useMarkAsReadScheduler } from '../../hooks/useMarkAsReadScheduler';
@@ -35,9 +33,9 @@ import { DEFAULT_MULTIPLE_FILES_MESSAGE_LIMIT, DEFAULT_UPLOAD_SIZE_LIMIT, VOICE_
3533
import { EmojiReactionListRoot, MenuRoot } from '../../../ui/ContextMenu';
3634

3735
// TODO: remove
38-
import { useReducer } from 'react';
3936
import { createStore } from '../../../utils/storeManager';
4037
import { initialState } from './initialState';
38+
import useSendbird from './hooks/useSendbird';
4139

4240
/**
4341
* SendbirdContext
@@ -84,9 +82,9 @@ const SendbirdContextManager = ({
8482
const { isMobile } = useMediaQueryContext();
8583
const [logger, setLogger] = useState(LoggerFactory(logLevel as LogLevel));
8684
const [pubSub] = useState(() => customPubSub ?? pubSubFactory<PUBSUB_TOPICS, SBUGlobalPubSubTopicPayloadUnion>());
87-
const [sdkStore, sdkDispatcher] = useReducer(sdkReducers, sdkInitialState);
88-
const [userStore, userDispatcher] = useReducer(userReducers, userInitialState);
89-
const [appInfoStore, appInfoDispatcher] = useReducer(appInfoReducers, appInfoInitialState);
85+
86+
const { state, actions } = useSendbird();
87+
const { sdkStore, userStore, appInfoStore } = state.stores;
9088

9189
const { configs, configsWithAppAttr, initDashboardConfigs } = useUIKitConfig();
9290

@@ -100,51 +98,42 @@ const SendbirdContextManager = ({
10098
sdk,
10199
logger,
102100
appInfoStore,
103-
appInfoDispatcher,
101+
actions,
104102
});
105103

106104
const utils: SendbirdProviderUtils = {
107105
updateMessageTemplatesInfo,
108106
getCachedTemplate,
109107
};
110108

111-
const reconnect = useConnect(
112-
{
113-
appId,
114-
userId,
115-
accessToken,
116-
isUserIdUsedForNickname,
117-
isMobile,
118-
},
119-
{
120-
logger,
121-
nickname,
122-
profileUrl,
123-
configureSession,
124-
customApiHost,
125-
customWebSocketHost,
126-
sdkInitParams,
127-
customExtensionParams,
128-
sdk,
129-
sdkDispatcher,
130-
userDispatcher,
131-
appInfoDispatcher,
132-
initDashboardConfigs,
133-
eventHandlers,
134-
initializeMessageTemplatesInfo,
135-
},
136-
);
137-
138-
useUnmount(() => {
139-
if (typeof sdk.disconnect === 'function') {
140-
disconnectSdk({
109+
// Reconnect when necessary
110+
useEffect(() => {
111+
if (sdkStore.sdk) {
112+
actions.connect({
113+
appId,
114+
userId,
115+
accessToken,
116+
isUserIdUsedForNickname,
117+
isMobile,
141118
logger,
142-
sdkDispatcher,
143-
userDispatcher,
144-
sdk,
119+
nickname,
120+
profileUrl,
121+
configureSession,
122+
customApiHost,
123+
customWebSocketHost,
124+
sdkInitParams,
125+
customExtensionParams,
126+
initDashboardConfigs,
127+
eventHandlers,
128+
initializeMessageTemplatesInfo,
145129
});
146130
}
147-
}, [sdk.disconnectWebSocket]);
131+
}, [appId, userId]);
132+
133+
// Disconnect on unmount
134+
useUnmount(() => {
135+
actions.disconnect({ logger });
136+
});
148137

149138
// to create a pubsub to communicate between parent and child
150139
useEffect(() => {
@@ -217,16 +206,13 @@ const SendbirdContextManager = ({
217206
return (
218207
<SendbirdContext.Provider
219208
value={{
220-
stores: {
221-
sdkStore,
222-
userStore,
223-
appInfoStore,
224-
},
209+
stores: state.stores,
210+
actions,
225211
// dispatchers: {
226212
// sdkDispatcher,
227213
// userDispatcher,
228214
// appInfoDispatcher,
229-
// reconnect,
215+
// reconnect, -> actions.connect
230216
// },
231217
config: {
232218
disableMarkAsDelivered,
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import { useContext, useMemo, useSyncExternalStore } from "react";
2+
import { SendbirdError } from "@sendbird/chat";
3+
4+
import { SendbirdContext } from "../SendbirdProvider";
5+
import { LoggerInterface } from "../../../Logger";
6+
import { MessageTemplatesInfo } from "../../../dux/appInfo/initialState";
7+
import { SendbirdState, WaitingTemplateKeyData } from "../../types";
8+
import { initSDK, setupSDK, updateAppInfoStore, updateSdkStore, updateUserStore } from "../../utils";
9+
10+
export const useSendbird = () => {
11+
const store = useContext(SendbirdContext);
12+
if (!store) throw new Error('useSendbird must be used within a SendbirdProvider');
13+
14+
const state = useSyncExternalStore(store.subscribe, store.getState);
15+
const actions = useMemo(() => ({
16+
// actionExample: () => store.setState((state): SendbirdState => ({
17+
// ...state,
18+
// example: true,
19+
// })),
20+
21+
/* AppInfo */
22+
initMessageTemplateInfo: ({ payload }: { payload: MessageTemplatesInfo }) => (
23+
store.setState((state): SendbirdState => (
24+
updateAppInfoStore(state, {
25+
messageTemplatesInfo: payload,
26+
waitingTemplateKeysMap: {},
27+
})
28+
))
29+
),
30+
upsertMessageTemplates: ({ payload }) => {
31+
const appInfoStore = state.stores.appInfoStore;
32+
const templatesInfo = appInfoStore.messageTemplatesInfo;
33+
if (!templatesInfo) return state; // Not initialized. Ignore.
34+
35+
const waitingTemplateKeysMap = { ...appInfoStore.waitingTemplateKeysMap };
36+
payload.forEach((templatesMapData) => {
37+
const { key, template } = templatesMapData;
38+
templatesInfo.templatesMap[key] = template;
39+
delete waitingTemplateKeysMap[key];
40+
});
41+
return store.setState((state): SendbirdState => (
42+
updateAppInfoStore(state, {
43+
waitingTemplateKeysMap,
44+
messageTemplatesInfo: templatesInfo,
45+
})
46+
));
47+
},
48+
upsertWaitingTemplateKeys: ({ payload }) => {
49+
const appInfoStore = state.stores.appInfoStore;
50+
const { keys, requestedAt } = payload;
51+
const waitingTemplateKeysMap = { ...appInfoStore.waitingTemplateKeysMap };
52+
keys.forEach((key) => {
53+
waitingTemplateKeysMap[key] = {
54+
erroredMessageIds: waitingTemplateKeysMap[key]?.erroredMessageIds ?? [],
55+
requestedAt,
56+
};
57+
});
58+
return store.setState((state): SendbirdState => (
59+
updateAppInfoStore(state, {
60+
waitingTemplateKeysMap,
61+
})
62+
));
63+
},
64+
markErrorWaitingTemplateKeys: ({ payload }) => {
65+
const appInfoStore = state.stores.appInfoStore;
66+
const { keys, messageId } = payload;
67+
const waitingTemplateKeysMap = { ...appInfoStore.waitingTemplateKeysMap };
68+
keys.forEach((key) => {
69+
const waitingTemplateKeyData: WaitingTemplateKeyData | undefined = waitingTemplateKeysMap[key];
70+
if (waitingTemplateKeyData && waitingTemplateKeyData.erroredMessageIds.indexOf(messageId) === -1) {
71+
waitingTemplateKeyData.erroredMessageIds.push(messageId);
72+
}
73+
});
74+
return store.setState((state): SendbirdState => (
75+
updateAppInfoStore(state, {
76+
waitingTemplateKeysMap,
77+
})
78+
));
79+
},
80+
81+
/* SDK */
82+
setSdkLoading: (payload) => (
83+
store.setState((state): SendbirdState => (
84+
updateSdkStore(state, {
85+
initialized: false,
86+
loading: payload,
87+
})
88+
))
89+
),
90+
sdkError: () => (
91+
store.setState((state): SendbirdState => (
92+
updateSdkStore(state, {
93+
initialized: false,
94+
loading: false,
95+
error: true,
96+
})
97+
))
98+
),
99+
initSdk: (payload) => (
100+
store.setState((state): SendbirdState => (
101+
updateSdkStore(state, {
102+
sdk: payload,
103+
initialized: true,
104+
loading: false,
105+
error: false,
106+
})
107+
))
108+
),
109+
resetSdk: () => (
110+
store.setState((state): SendbirdState => (
111+
updateSdkStore(state, {
112+
sdk: null,
113+
initialized: false,
114+
loading: false,
115+
error: false,
116+
})
117+
))
118+
),
119+
120+
/* User */
121+
initUser: (payload) => (
122+
store.setState((state): SendbirdState => (
123+
updateUserStore(state, {
124+
initialized: true,
125+
loading: false,
126+
user: payload,
127+
})
128+
))
129+
),
130+
resetUser: () => (
131+
store.setState((state): SendbirdState => (
132+
updateUserStore(state, {
133+
initialized: false,
134+
loading: false,
135+
user: null,
136+
})
137+
))
138+
),
139+
updateUserInfo: (payload) => (
140+
store.setState((state): SendbirdState => (
141+
updateUserStore(state, {
142+
user: payload,
143+
})
144+
))
145+
),
146+
147+
/* Connection */
148+
connect: async (params) => {
149+
const {
150+
logger,
151+
userId,
152+
appId,
153+
accessToken,
154+
nickname,
155+
profileUrl,
156+
isMobile,
157+
sdkInitParams,
158+
customApiHost,
159+
customWebSocketHost,
160+
customExtensionParams,
161+
eventHandlers,
162+
initializeMessageTemplatesInfo,
163+
configureSession,
164+
initDashboardConfigs,
165+
} = params;
166+
167+
/* Clean up previous ws connection */
168+
await actions.disconnect({ logger });
169+
170+
const sdk = initSDK({
171+
appId,
172+
customApiHost,
173+
customWebSocketHost,
174+
sdkInitParams,
175+
});
176+
177+
setupSDK(sdk, {
178+
logger,
179+
isMobile,
180+
customExtensionParams,
181+
sessionHandler: configureSession ? configureSession(sdk) : undefined,
182+
});
183+
184+
actions.setSdkLoading(true);
185+
186+
try {
187+
const user = await sdk.connect(userId, accessToken);
188+
actions.initUser(user);
189+
190+
if (nickname || profileUrl) {
191+
await sdk.updateCurrentUserInfo({
192+
nickname: nickname || user.nickname || '',
193+
profileUrl: profileUrl || user.profileUrl,
194+
});
195+
}
196+
197+
await initializeMessageTemplatesInfo?.(sdk);
198+
await initDashboardConfigs?.(sdk);
199+
200+
store.setState((state) => ({
201+
...state,
202+
stores: {
203+
...state.stores,
204+
sdkStore: {
205+
...state.stores.sdkStore,
206+
sdk,
207+
initialized: true,
208+
loading: false,
209+
},
210+
},
211+
}));
212+
213+
eventHandlers?.connection?.onConnected?.(user);
214+
} catch (error) {
215+
const sendbirdError = error as SendbirdError;
216+
actions.resetSdk();
217+
actions.resetUser();
218+
logger.error?.('SendbirdProvider | useSendbird/connect failed', sendbirdError);
219+
eventHandlers?.connection?.onFailed?.(sendbirdError);
220+
}
221+
},
222+
disconnect: async ({ logger }: { logger: LoggerInterface }) => {
223+
actions.setSdkLoading(true);
224+
225+
const sdk = state.stores.sdkStore.sdk;
226+
227+
if (sdk?.disconnectWebSocket) {
228+
await sdk.disconnectWebSocket();
229+
}
230+
231+
actions.resetSdk();
232+
actions.resetUser();
233+
logger.info?.('SendbirdProvider | useSendbird/disconnect completed');
234+
},
235+
}), [store]);
236+
237+
return { state, actions };
238+
};
239+
240+
export default useSendbird;

src/lib/Sendbird/context/initialState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ const stores: SendbirdStateStore = {
100100
},
101101
appInfoStore: {
102102
messageTemplatesInfo: undefined,
103-
waitingTemplateKeysMap: {},
103+
waitingTemplateKeysMap: undefined,
104104
},
105105
};
106106

0 commit comments

Comments
 (0)