Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/lib/Sendbird/context/SendbirdProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ const SendbirdContextManager = ({

// Disconnect on unmount
useUnmount(() => {
actions.disconnect({ logger });
}, [sdkStore]);
if (sdkInitialized && sdkStore?.sdk) {
actions.disconnect({ logger });
}
}, [sdkInitialized, sdkStore?.sdk]);

// should move to reducer
const [currentTheme, setCurrentTheme] = useState(theme);
Expand Down
237 changes: 133 additions & 104 deletions src/lib/Sendbird/context/hooks/useSendbird.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useSyncExternalStore } from 'use-sync-external-store/shim';
import { useContext, useMemo } from 'react';
import { useCallback, useContext, useMemo } from 'react';
import { SendbirdError, User } from '@sendbird/chat';

import { SendbirdContext } from '../SendbirdContext';
Expand All @@ -11,27 +11,20 @@ const NO_CONTEXT_ERROR = 'No sendbird state value available. Make sure you are r
export const useSendbird = () => {
const store = useContext(SendbirdContext);
if (!store) throw new Error(NO_CONTEXT_ERROR);

const state: SendbirdState = useSyncExternalStore(store.subscribe, store.getState);
const actions = useMemo(() => ({
/* Example: How to set the state basically */
// exampleAction: () => {
// store.setState((state): SendbirdState => ({
// ...state,
// example: true,
// })),
// },

/* AppInfo */
initMessageTemplateInfo: ({ payload }: { payload: MessageTemplatesInfo }) => {

/* AppInfo */
const appInfoActions = {
initMessageTemplateInfo: useCallback(({ payload }: { payload: MessageTemplatesInfo }) => {
store.setState((state): SendbirdState => (
updateAppInfoStore(state, {
messageTemplatesInfo: payload,
waitingTemplateKeysMap: {},
})
));
},
upsertMessageTemplates: ({ payload }) => {
}, [store]),

upsertMessageTemplates: useCallback(({ payload }) => {
const appInfoStore = state.stores.appInfoStore;
const templatesInfo = appInfoStore.messageTemplatesInfo;
if (!templatesInfo) return state; // Not initialized. Ignore.
Expand All @@ -48,8 +41,9 @@ export const useSendbird = () => {
messageTemplatesInfo: templatesInfo,
})
));
},
upsertWaitingTemplateKeys: ({ payload }) => {
}, [store, state.stores.appInfoStore]),

upsertWaitingTemplateKeys: useCallback(({ payload }) => {
const appInfoStore = state.stores.appInfoStore;
const { keys, requestedAt } = payload;
const waitingTemplateKeysMap = { ...appInfoStore.waitingTemplateKeysMap };
Expand All @@ -64,8 +58,9 @@ export const useSendbird = () => {
waitingTemplateKeysMap,
})
));
},
markErrorWaitingTemplateKeys: ({ payload }) => {
}, [store, state.stores.appInfoStore]),

markErrorWaitingTemplateKeys: useCallback(({ payload }) => {
const appInfoStore = state.stores.appInfoStore;
const { keys, messageId } = payload;
const waitingTemplateKeysMap = { ...appInfoStore.waitingTemplateKeysMap };
Expand All @@ -80,27 +75,31 @@ export const useSendbird = () => {
waitingTemplateKeysMap,
})
));
},
}, [store, state.stores.appInfoStore]),
};

/* SDK */
setSdkLoading: (payload) => {
/* SDK */
const sdkActions = {
setSdkLoading: useCallback((payload) => {
store.setState((state): SendbirdState => (
updateSdkStore(state, {
initialized: false,
loading: payload,
})
));
},
sdkError: () => {
}, [store]),

sdkError: useCallback(() => {
store.setState((state): SendbirdState => (
updateSdkStore(state, {
initialized: false,
loading: false,
error: true,
})
));
},
initSdk: (payload) => {
}, [store]),

initSdk: useCallback((payload) => {
store.setState((state): SendbirdState => (
updateSdkStore(state, {
sdk: payload,
Expand All @@ -109,8 +108,9 @@ export const useSendbird = () => {
error: false,
})
));
},
resetSdk: () => {
}, [store]),

resetSdk: useCallback(() => {
store.setState((state): SendbirdState => (
updateSdkStore(state, {
sdk: {} as SdkStore['sdk'],
Expand All @@ -119,113 +119,142 @@ export const useSendbird = () => {
error: false,
})
));
},
}, [store]),
};

/* User */
initUser: (payload) => {
/* User */
const userActions = {
initUser: useCallback((payload) => {
store.setState((state): SendbirdState => (
updateUserStore(state, {
initialized: true,
loading: false,
user: payload,
})
));
},
resetUser: () => {
}, [store]),

resetUser: useCallback(() => {
store.setState((state): SendbirdState => (
updateUserStore(state, {
initialized: false,
loading: false,
user: {} as User,
})
));
},
updateUserInfo: (payload: User) => {
}, [store]),

updateUserInfo: useCallback((payload: User) => {
store.setState((state): SendbirdState => (
updateUserStore(state, {
user: payload,
})
));
},

/* Connection */
connect: async (params) => {
const {
logger,
userId,
appId,
accessToken,
nickname,
profileUrl,
isMobile,
sdkInitParams,
customApiHost,
customWebSocketHost,
customExtensionParams,
eventHandlers,
initializeMessageTemplatesInfo,
configureSession,
initDashboardConfigs,
} = params;

// clean up previous ws connection
await actions.disconnect({ logger });

const sdk = initSDK({
appId,
customApiHost,
customWebSocketHost,
sdkInitParams,
});
}, [store]),
};

setupSDK(sdk, {
logger,
isMobile,
customExtensionParams,
sessionHandler: configureSession ? configureSession(sdk) : undefined,
});
/* Connection */
const disconnect = useCallback(async ({ logger }: { logger: LoggerInterface }) => {
sdkActions.setSdkLoading(true);

actions.setSdkLoading(true);
const sdk = state.stores.sdkStore.sdk;

try {
const user = await sdk.connect(userId, accessToken);
actions.initUser(user);
if (sdk?.disconnectWebSocket) {
await sdk.disconnectWebSocket();
}

if (nickname || profileUrl) {
await sdk.updateCurrentUserInfo({
nickname: nickname || user.nickname || '',
profileUrl: profileUrl || user.profileUrl,
});
}
sdkActions.resetSdk();
userActions.resetUser();
logger.info?.('SendbirdProvider | useSendbird/disconnect completed');
}, [
store,
state.stores.sdkStore?.sdk,
sdkActions,
userActions,
]);

await initializeMessageTemplatesInfo?.(sdk);
await initDashboardConfigs?.(sdk);
const connect = useCallback(async (params) => {
const {
logger,
userId,
appId,
accessToken,
nickname,
profileUrl,
isMobile,
sdkInitParams,
customApiHost,
customWebSocketHost,
customExtensionParams,
eventHandlers,
initializeMessageTemplatesInfo,
configureSession,
initDashboardConfigs,
} = params;

actions.initSdk(sdk);
// clean up previous ws connection
await disconnect({ logger });

eventHandlers?.connection?.onConnected?.(user);
} catch (error) {
const sendbirdError = error as SendbirdError;
actions.resetSdk();
actions.resetUser();
logger.error?.('SendbirdProvider | useSendbird/connect failed', sendbirdError);
eventHandlers?.connection?.onFailed?.(sendbirdError);
}
},
disconnect: async ({ logger }: { logger: LoggerInterface }) => {
actions.setSdkLoading(true);
const sdk = initSDK({
appId,
customApiHost,
customWebSocketHost,
sdkInitParams,
});

setupSDK(sdk, {
logger,
isMobile,
customExtensionParams,
sessionHandler: configureSession ? configureSession(sdk) : undefined,
});

const sdk = state.stores.sdkStore.sdk;
sdkActions.setSdkLoading(true);

if (sdk?.disconnectWebSocket) {
await sdk.disconnectWebSocket();
try {
const user = await sdk.connect(userId, accessToken);
userActions.initUser(user);

if (nickname || profileUrl) {
await sdk.updateCurrentUserInfo({
nickname: nickname || user.nickname || '',
profileUrl: profileUrl || user.profileUrl,
});
}

actions.resetSdk();
actions.resetUser();
logger.info?.('SendbirdProvider | useSendbird/disconnect completed');
},
}), [store, state.stores.sdkStore?.sdk, state.stores.appInfoStore]);
await initializeMessageTemplatesInfo?.(sdk);
await initDashboardConfigs?.(sdk);

sdkActions.initSdk(sdk);

eventHandlers?.connection?.onConnected?.(user);
} catch (error) {
const sendbirdError = error as SendbirdError;
sdkActions.resetSdk();
userActions.resetUser();
logger.error?.('SendbirdProvider | useSendbird/connect failed', sendbirdError);
eventHandlers?.connection?.onFailed?.(sendbirdError);
}
}, [
store,
sdkActions,
userActions,
disconnect,
]);

const actions = useMemo(() => ({
...appInfoActions,
...sdkActions,
...userActions,
disconnect,
connect,
}), [
appInfoActions,
sdkActions,
userActions,
disconnect,
connect,
]);

return { state, actions };
};
Expand Down
36 changes: 23 additions & 13 deletions src/modules/CreateChannel/context/useCreateChannel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useSyncExternalStore } from 'use-sync-external-store/shim';
import { useContext, useMemo } from 'react';
import { useCallback, useContext, useMemo } from 'react';
import { CreateChannelContext, CreateChannelState } from './CreateChannelProvider';
import { CHANNEL_TYPE } from '../types';
import { getCreateGroupChannel } from '../../../lib/selectors';
Expand All @@ -10,20 +10,30 @@ const useCreateChannel = () => {
const sendbirdStore = useSendbirdStateContext();
if (!store) throw new Error('useCreateChannel must be used within a CreateChannelProvider');

const setPageStep = useCallback((pageStep: number) => {
store.setState(state => ({ ...state, pageStep }));
}, [store]);

const setType = useCallback((type: CHANNEL_TYPE) => {
store.setState(state => ({ ...state, type }));
}, [store]);

const createChannel = useCallback((...params: Parameters<ReturnType<typeof getCreateGroupChannel>>) => {
const createChannel = getCreateGroupChannel(sendbirdStore);

return createChannel(...params);
}, [sendbirdStore]);

const state: CreateChannelState = useSyncExternalStore(store.subscribe, store.getState);
const actions = useMemo(() => ({
setPageStep: (pageStep: number) => store.setState(state => ({
...state,
pageStep,
})),

setType: (type: CHANNEL_TYPE) => store.setState(state => ({
...state,
type,
})),

createChannel: getCreateGroupChannel(sendbirdStore),
}), [store]);
setPageStep,
setType,
createChannel,
}), [
setPageStep,
setType,
createChannel,
]);

return { state, actions };
};
Expand Down
Loading