Skip to content

Commit 2a4a642

Browse files
chohongmbang9
andauthored
1 parent 5d7aa1e commit 2a4a642

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1147
-36
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
},
4141
"rules": {
4242
"no-unused-expressions": "off",
43+
"no-await-in-loop": "off",
4344
// Not to make ESLint complains a type is only being used as type but recognized as a unused variable
4445
// https://stackoverflow.com/questions/57802057/eslint-configuring-no-unused-vars-for-typescript
4546
"no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off",

jest.config.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,9 @@ module.exports = {
178178
// transform: undefined,
179179

180180
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
181-
// transformIgnorePatterns: [
182-
// "/node_modules/"
183-
// ],
184-
181+
transformIgnorePatterns: [
182+
'/node_modules/(?!(?:@sendbird/(react-uikit-message-template-view|uikit-message-template))/)'
183+
]
185184
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
186185
// unmockedModulePathPatterns: undefined,
187186

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@
6767
"react-dom": "^16.8.6 || ^17.0.0 || ^18.0.0"
6868
},
6969
"dependencies": {
70-
"@sendbird/chat": "^4.10.10",
70+
"@sendbird/chat": "^4.11.0",
71+
"@sendbird/react-uikit-message-template-view": "0.0.1-alpha.65",
7172
"@sendbird/uikit-tools": "0.0.1-alpha.65",
7273
"css-vars-ponyfill": "^2.3.2",
7374
"date-fns": "^2.16.1",

rollup.module-exports.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ export default {
201201
'ui/MessageSearchFileItem': 'src/ui/MessageSearchFileItem/index.tsx',
202202
'ui/MessageSearchItem': 'src/ui/MessageSearchItem/index.tsx',
203203
'ui/MessageStatus': 'src/ui/MessageStatus/index.tsx',
204+
'ui/MessageTemplate': 'src/ui/MessageTemplate/index.tsx',
205+
'ui/TemplateMessageItemBody': 'src/ui/TemplateMessageItemBody/index.tsx',
206+
'ui/FallbackTemplateMessageItemBody.tsx': 'src/ui/TemplateMessageItemBody/FallbackTemplateMessageItemBody.tsx',
207+
'ui/LoadingTemplateMessageItemBody.tsx': 'src/ui/TemplateMessageItemBody/LoadingTemplateMessageItemBody.tsx',
204208
'ui/Modal': 'src/ui/Modal/index.tsx',
205209
'ui/OGMessageItemBody': 'src/ui/OGMessageItemBody/index.tsx',
206210
'ui/OpenChannelAdminMessage': 'src/ui/OpenChannelAdminMessage/index.tsx',

src/lib/Sendbird.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import useTheme from './hooks/useTheme';
1212

1313
import sdkReducers from './dux/sdk/reducers';
1414
import userReducers from './dux/user/reducers';
15+
import appInfoReducers from './dux/appInfo/reducers';
1516

1617
import sdkInitialState from './dux/sdk/initialState';
1718
import userInitialState from './dux/user/initialState';
19+
import appInfoInitialState from './dux/appInfo/initialState';
1820

1921
import useOnlineStatus from './hooks/useOnlineStatus';
2022
import useConnect from './hooks/useConnect';
@@ -44,13 +46,14 @@ import {
4446
CommonUIKitConfigProps,
4547
SendbirdChatInitParams,
4648
CustomExtensionParams,
47-
SBUEventHandlers,
49+
SBUEventHandlers, SendbirdProviderUtils,
4850
} from './types';
4951
import { GlobalModalProvider } from '../hooks/useModal';
5052
import { RenderUserProfileProps } from '../types';
5153
import PUBSUB_TOPICS, { SBUGlobalPubSub, SBUGlobalPubSubTopicPayloadUnion } from './pubSub/topics';
5254
import { EmojiManager } from './emojiManager';
5355
import { uikitConfigStorage } from './utils/uikitConfigStorage';
56+
import useMessageTemplateUtils from './hooks/useMessageTemplateUtils';
5457

5558
export { useSendbirdStateContext } from '../hooks/useSendbirdStateContext';
5659

@@ -189,6 +192,7 @@ const SendbirdSDK = ({
189192
const [pubSub] = useState(() => customPubSub ?? pubSubFactory<PUBSUB_TOPICS, SBUGlobalPubSubTopicPayloadUnion>());
190193
const [sdkStore, sdkDispatcher] = useReducer(sdkReducers, sdkInitialState);
191194
const [userStore, userDispatcher] = useReducer(userReducers, userInitialState);
195+
const [appInfoStore, appInfoDispatcher] = useReducer(appInfoReducers, appInfoInitialState);
192196

193197
const { configs, configsWithAppAttr, initDashboardConfigs } = useUIKitConfig();
194198
const sdkInitialized = sdkStore.initialized;
@@ -200,6 +204,19 @@ const SendbirdSDK = ({
200204

201205
useTheme(colorSet);
202206

207+
const {
208+
getCachedTemplate,
209+
updateMessageTemplatesInfo,
210+
initializeMessageTemplatesInfo,
211+
} = useMessageTemplateUtils({
212+
sdk, logger, appInfoStore, appInfoDispatcher,
213+
});
214+
215+
const utils: SendbirdProviderUtils = {
216+
updateMessageTemplatesInfo,
217+
getCachedTemplate,
218+
};
219+
203220
const reconnect = useConnect({
204221
appId,
205222
userId,
@@ -218,8 +235,10 @@ const SendbirdSDK = ({
218235
sdk,
219236
sdkDispatcher,
220237
userDispatcher,
238+
appInfoDispatcher,
221239
initDashboardConfigs,
222240
eventHandlers,
241+
initializeMessageTemplatesInfo,
223242
});
224243

225244
useUnmount(() => {
@@ -320,10 +339,12 @@ const SendbirdSDK = ({
320339
stores: {
321340
sdkStore,
322341
userStore,
342+
appInfoStore,
323343
},
324344
dispatchers: {
325345
sdkDispatcher,
326346
userDispatcher,
347+
appInfoDispatcher,
327348
reconnect,
328349
},
329350
config: {
@@ -396,6 +417,7 @@ const SendbirdSDK = ({
396417
},
397418
eventHandlers,
398419
emojiManager,
420+
utils,
399421
}}
400422
>
401423
<MediaQueryProvider logger={logger} breakpoint={breakpoint}>

src/lib/dux/appInfo/actionTypes.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { CreateAction } from '../../../utils/typeHelpers/reducers/createAction';
2+
import { MessageTemplatesInfo, ProcessedMessageTemplate } from './initialState';
3+
4+
export const APP_INFO_ACTIONS = {
5+
INITIALIZE_MESSAGE_TEMPLATES_INFO: 'INITIALIZE_MESSAGE_TEMPLATES_INFO',
6+
UPSERT_MESSAGE_TEMPLATE: 'UPSERT_MESSAGE_TEMPLATE',
7+
UPSERT_WAITING_TEMPLATE_KEY: 'UPSERT_WAITING_TEMPLATE_KEY',
8+
MARK_ERROR_WAITING_TEMPLATE_KEY: 'MARK_ERROR_WAITING_TEMPLATE_KEY',
9+
} as const;
10+
11+
export type TemplatesMapData = {
12+
key: string;
13+
template: ProcessedMessageTemplate;
14+
};
15+
16+
type APP_INFO_PAYLOAD_TYPES = {
17+
[APP_INFO_ACTIONS.INITIALIZE_MESSAGE_TEMPLATES_INFO]: MessageTemplatesInfo,
18+
[APP_INFO_ACTIONS.UPSERT_MESSAGE_TEMPLATE]: TemplatesMapData,
19+
[APP_INFO_ACTIONS.UPSERT_WAITING_TEMPLATE_KEY]: { key: string, requestedAt: number },
20+
[APP_INFO_ACTIONS.MARK_ERROR_WAITING_TEMPLATE_KEY]: { key: string },
21+
};
22+
23+
export type AppInfoActionTypes = CreateAction<APP_INFO_PAYLOAD_TYPES>;

src/lib/dux/appInfo/initialState.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export type ProcessedMessageTemplate = {
2+
uiTemplate: string; // This is stringified ui_template.
3+
colorVariables?: Record<string, string>;
4+
};
5+
6+
export interface MessageTemplatesInfo {
7+
token: string; // This server-side token gets updated on every CRUD operation on message template table.
8+
templatesMap: Record<string, ProcessedMessageTemplate>;
9+
}
10+
11+
export interface WaitingTemplateKeyData {
12+
requestedAt: number;
13+
isError: boolean;
14+
}
15+
16+
export interface AppInfoStateType {
17+
messageTemplatesInfo?: MessageTemplatesInfo;
18+
/**
19+
* This represents template keys that are currently waiting for its fetch response.
20+
* Whenever initialized, request succeeds or fails, it needs to be updated.
21+
*/
22+
waitingTemplateKeysMap: Record<string, WaitingTemplateKeyData>;
23+
}
24+
25+
const initialState: AppInfoStateType = {
26+
waitingTemplateKeysMap: {},
27+
};
28+
29+
export default initialState;

src/lib/dux/appInfo/reducers.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { match } from 'ts-pattern';
2+
import { AppInfoStateType, WaitingTemplateKeyData } from './initialState';
3+
import { APP_INFO_ACTIONS, AppInfoActionTypes } from './actionTypes';
4+
5+
export default function reducer(state: AppInfoStateType, action: AppInfoActionTypes): AppInfoStateType {
6+
return match(action)
7+
.with(
8+
{ type: APP_INFO_ACTIONS.INITIALIZE_MESSAGE_TEMPLATES_INFO },
9+
({ payload }) => {
10+
return {
11+
messageTemplatesInfo: payload,
12+
waitingTemplateKeysMap: {},
13+
};
14+
})
15+
.with(
16+
{ type: APP_INFO_ACTIONS.UPSERT_MESSAGE_TEMPLATE },
17+
({ payload }) => {
18+
const templatesInfo = state.messageTemplatesInfo;
19+
if (!templatesInfo) return state; // Not initialized. Ignore.
20+
21+
const { key, template } = payload;
22+
templatesInfo.templatesMap[key] = template;
23+
24+
delete state.waitingTemplateKeysMap[key];
25+
26+
return {
27+
...state,
28+
messageTemplatesInfo: templatesInfo,
29+
};
30+
})
31+
.with(
32+
{ type: APP_INFO_ACTIONS.UPSERT_WAITING_TEMPLATE_KEY },
33+
({ payload }) => {
34+
const { key, requestedAt } = payload;
35+
state.waitingTemplateKeysMap[key] = {
36+
requestedAt,
37+
isError: false,
38+
};
39+
return { ...state };
40+
})
41+
.with(
42+
{ type: APP_INFO_ACTIONS.MARK_ERROR_WAITING_TEMPLATE_KEY },
43+
({ payload }) => {
44+
const { key } = payload;
45+
const waitingTemplateKeyData: WaitingTemplateKeyData | undefined = state.waitingTemplateKeysMap[key];
46+
if (waitingTemplateKeyData) {
47+
waitingTemplateKeyData.isError = true;
48+
}
49+
return { ...state };
50+
})
51+
.otherwise(() => {
52+
return state;
53+
});
54+
}

src/lib/dux/appInfo/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ProcessedMessageTemplate } from './initialState';
2+
import { SendbirdMessageTemplate } from '../../../ui/TemplateMessageItemBody/types';
3+
4+
/**
5+
* Takes JSON parsed template and then returns processed message template for storing it in global state.
6+
*/
7+
export const getProcessedTemplate = (parsedTemplate: SendbirdMessageTemplate): ProcessedMessageTemplate => {
8+
return {
9+
uiTemplate: JSON.stringify(parsedTemplate.ui_template.body.items),
10+
colorVariables: parsedTemplate.color_variables,
11+
};
12+
};
13+
14+
export const getProcessedTemplates = (
15+
parsedTemplates: SendbirdMessageTemplate[],
16+
): Record<string, ProcessedMessageTemplate> => {
17+
const processedTemplates = {};
18+
parsedTemplates.forEach((template) => {
19+
processedTemplates[template.key] = getProcessedTemplate(template);
20+
});
21+
return processedTemplates;
22+
};

src/lib/hooks/useConnect/__test__/data.mocks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const mockSdk = {
3636

3737
export const mockSdkDispatcher = jest.fn() as unknown as ConnectTypes['sdkDispatcher'];
3838
export const mockUserDispatcher = jest.fn() as unknown as ConnectTypes['userDispatcher'];
39+
export const mockAppInfoDispatcher = jest.fn() as unknown as ConnectTypes['appInfoDispatcher'];
3940
export const mockInitDashboardConfigs = jest.fn().mockImplementation(() => Promise.resolve({})) as unknown as ConnectTypes['initDashboardConfigs'];
4041

4142
export const defaultStaticParams: StaticTypes = {
@@ -45,7 +46,9 @@ export const defaultStaticParams: StaticTypes = {
4546
logger: LoggerFactory('all'),
4647
sdkDispatcher: mockSdkDispatcher,
4748
userDispatcher: mockUserDispatcher,
49+
appInfoDispatcher: mockAppInfoDispatcher,
4850
initDashboardConfigs: mockInitDashboardConfigs,
51+
initializeMessageTemplatesInfo: jest.fn().mockImplementation(() => Promise.resolve()),
4952
};
5053

5154
export const defaultTriggerParams: TriggerTypes = {

src/lib/hooks/useConnect/connect.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export async function connect({
66
logger,
77
sdkDispatcher,
88
userDispatcher,
9+
appInfoDispatcher,
910
initDashboardConfigs,
1011
userId,
1112
appId,
@@ -21,6 +22,7 @@ export async function connect({
2122
isMobile,
2223
eventHandlers,
2324
isUserIdUsedForNickname,
25+
initializeMessageTemplatesInfo,
2426
}: ConnectTypes): Promise<void> {
2527
await disconnectSdk({
2628
logger,
@@ -32,6 +34,7 @@ export async function connect({
3234
logger,
3335
sdkDispatcher,
3436
userDispatcher,
37+
appInfoDispatcher,
3538
initDashboardConfigs,
3639
userId,
3740
appId,
@@ -46,5 +49,6 @@ export async function connect({
4649
isMobile,
4750
eventHandlers,
4851
isUserIdUsedForNickname,
52+
initializeMessageTemplatesInfo,
4953
});
5054
}

src/lib/hooks/useConnect/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ export default function useConnect(triggerTypes: TriggerTypes, staticTypes: Stat
1515
sdk,
1616
sdkDispatcher,
1717
userDispatcher,
18+
appInfoDispatcher,
1819
initDashboardConfigs,
1920
sdkInitParams,
2021
customExtensionParams,
2122
eventHandlers,
23+
initializeMessageTemplatesInfo,
2224
} = staticTypes;
2325
logger?.info?.('SendbirdProvider | useConnect', { ...triggerTypes, ...staticTypes });
2426

@@ -50,12 +52,14 @@ export default function useConnect(triggerTypes: TriggerTypes, staticTypes: Stat
5052
sdk,
5153
sdkDispatcher,
5254
userDispatcher,
55+
appInfoDispatcher,
5356
initDashboardConfigs,
5457
isUserIdUsedForNickname,
5558
sdkInitParams,
5659
customExtensionParams,
5760
isMobile,
5861
eventHandlers,
62+
initializeMessageTemplatesInfo,
5963
}).catch(error => {
6064
logger?.error?.('SendbirdProvider | useConnect/useEffect', error);
6165
});
@@ -78,12 +82,14 @@ export default function useConnect(triggerTypes: TriggerTypes, staticTypes: Stat
7882
sdk,
7983
sdkDispatcher,
8084
userDispatcher,
85+
appInfoDispatcher,
8186
initDashboardConfigs,
8287
isUserIdUsedForNickname,
8388
sdkInitParams,
8489
customExtensionParams,
8590
isMobile,
8691
eventHandlers,
92+
initializeMessageTemplatesInfo,
8793
});
8894
} catch (error) {
8995
logger?.error?.('SendbirdProvider | useConnect/reconnect/useCallback', error);

src/lib/hooks/useConnect/setupConnection.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export async function setUpConnection({
7575
customExtensionParams,
7676
isMobile = false,
7777
eventHandlers,
78+
initializeMessageTemplatesInfo,
7879
}: SetupConnectionTypes): Promise<void> {
7980
return new Promise((resolve, reject) => {
8081
logger?.info?.('SendbirdProvider | useConnect/setupConnection/init', { userId, appId });
@@ -115,13 +116,22 @@ export async function setUpConnection({
115116
},
116117
customExtensionParams,
117118
);
119+
118120
newSdk.addExtension('sb_uikit', APP_VERSION_STRING);
119121

120-
const connectCbSucess = async (user: User) => {
121-
logger?.info?.('SendbirdProvider | useConnect/setupConnection/connectCbSucess', user);
122+
const connectCbSuccess = async (user: User) => {
123+
logger?.info?.('SendbirdProvider | useConnect/setupConnection/connectCbSuccess', user);
122124
sdkDispatcher({ type: INIT_SDK, payload: newSdk });
123125
userDispatcher({ type: INIT_USER, payload: user });
124126

127+
try {
128+
await initializeMessageTemplatesInfo(newSdk);
129+
} catch (error) {
130+
logger?.error?.('SendbirdProvider | useConnect/setupConnection/upsertMessageTemplateListInLocalStorage failed', {
131+
error,
132+
});
133+
}
134+
125135
initDashboardConfigs(newSdk)
126136
.then(config => {
127137
logger?.info?.('SendbirdProvider | useConnect/setupConnection/getUIKitConfiguration success', {
@@ -178,7 +188,7 @@ export async function setUpConnection({
178188

179189
logger?.info?.(`SendbirdProvider | useConnect/setupConnection/connect connecting using ${accessToken ?? userId}`);
180190
newSdk.connect(userId, accessToken)
181-
.then((res) => connectCbSucess(res))
191+
.then((res) => connectCbSuccess(res))
182192
.catch((err) => connectCbError(err));
183193
} else {
184194
const errorMessage = getMissingParamError({ userId, appId });

0 commit comments

Comments
 (0)