Skip to content

Commit

Permalink
feat: added a user sync step for manually configuring session
Browse files Browse the repository at this point in the history
  • Loading branch information
bang9 committed May 27, 2024
1 parent 17963b5 commit e272e38
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 35 deletions.
9 changes: 9 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ module.exports = {
'includeRoles': ['button']
},
],
'@typescript-eslint/no-unused-vars': [
1,
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
},
settings: {
react: {
Expand Down
2 changes: 0 additions & 2 deletions src/components/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ const Chat = () => {
stores.sdkStore.initialized,
]);



const onBeforeSendMessage = <
T extends UserMessageCreateParams | FileMessageCreateParams
>(
Expand Down
8 changes: 2 additions & 6 deletions src/const.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import SendbirdChat, { SessionHandler } from '@sendbird/chat';
import { type SendbirdGroupChat } from '@sendbird/chat/groupChannel';
import { type SendbirdOpenChat } from '@sendbird/chat/openChannel';
import { SessionHandler } from '@sendbird/chat';
import React from 'react';

import { StringSet } from '@uikit/ui/Label/stringSet';
Expand Down Expand Up @@ -67,9 +65,7 @@ export const DEFAULT_CONSTANT = {
enableResetHistoryOnConnect: false,
} satisfies Partial<Constant>;

type ConfigureSession = (
sdk: SendbirdChat | SendbirdGroupChat | SendbirdOpenChat
) => SessionHandler;
type ConfigureSession = () => SessionHandler;

type MessageData = {
suggested_replies?: string[];
Expand Down
83 changes: 60 additions & 23 deletions src/context/WidgetSettingContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ interface WidgetSession {
strategy: 'auto' | 'manual';
userId: string;
expireAt: number;
// channelUrl is optional and is dynamically generated for a manual strategy.
channelUrl?: string;
// sessionToken is optional and is dynamically generated by configureSession provided by the user for a manual strategy.
sessionToken?: string;
// channelUrl is optional and is dynamically generated for a legacy manual strategy.
channelUrl?: string;
}

export interface BotStyle {
Expand Down Expand Up @@ -85,7 +85,9 @@ export const WidgetSettingProvider = ({
botId,
});

const reuseCachedSession = ((cache: typeof cachedSession): cache is NonNullable<typeof cachedSession> => {
const reuseCachedSession = ((
cache: typeof cachedSession
): cache is NonNullable<typeof cachedSession> => {
if (!cache || cache.strategy !== strategy) return false;
if (cache.strategy === 'manual') {
// NOTE: There is no need to check the expiration of the session if it is managed manually.
Expand All @@ -98,53 +100,88 @@ export const WidgetSettingProvider = ({
return false;
})(cachedSession);

const response = await getWidgetSetting({
const getConfigureSessionParams = async () => {
if (strategy !== 'manual') return undefined;

if (injectedUserId && configureSession && !reuseCachedSession) {
const sessionHandler = configureSession();
if (sessionHandler.onSessionTokenRequired) {
try {
const token = await new Promise(
sessionHandler.onSessionTokenRequired
);
if (token) return { userId: injectedUserId, sessionKey: token };
} catch {
// NO-OP
}
}
}
return undefined;
};

const widgetSettingParams = {
host: apiHost,
appId,
botId,
createUserAndChannel: reuseCachedSession ? false : strategy === 'auto',
});
createUserAndChannel: !reuseCachedSession,
...(await getConfigureSessionParams()),
};
const response = await getWidgetSetting(widgetSettingParams);
setBotStyle(response.botStyle);

if (reuseCachedSession) {
setWidgetSession({
strategy: sessionStrategy,
userId: cachedSession.userId,
expireAt: cachedSession.expireAt,
channelUrl: cachedSession.channelUrl,
sessionToken: cachedSession.sessionToken,
channelUrl: cachedSession.channelUrl,
});
} else {
if (sessionStrategy === 'auto' && response.user && response.channel) {
const session = {
strategy: sessionStrategy,
expireAt: response.user.expireAt,
userId: response.user.userId,
expireAt: response.user.expireAt,
sessionToken: response.user.sessionToken,
channelUrl: response.channel.channelUrl,
};
setWidgetSession(session);
saveWidgetSessionCache({ appId, botId, data: session });
}

/**
* NOTE: We don't fully initialize the manual strategy session here.
* After the uikit is initialized, we should call the `initManualSession` function.
* */
if (sessionStrategy === 'manual' && injectedUserId) {
const session = {
strategy: sessionStrategy,
userId: injectedUserId,
sessionToken: undefined,
channelUrl: undefined,
expireAt: 0,
};
setWidgetSession(session);
if (response.channel) {
const session = {
strategy: sessionStrategy,
userId: injectedUserId,
expireAt: getDateNDaysLater(30),
sessionToken: widgetSettingParams.sessionKey,
channelUrl: response.channel.channelUrl,
};
setWidgetSession(session);
saveWidgetSessionCache({ appId, botId, data: session });
} else {
/**
* TODO: Remove this after the widget_setting deployed to all region.
* NOTE: We don't fully initialize the legacy manual strategy session here.
* After the uikit is initialized, we should call the `initManualSession` function.
* */
const session = {
strategy: sessionStrategy,
userId: injectedUserId,
expireAt: 0,
sessionToken: widgetSettingParams.sessionKey,
channelUrl: undefined,
};
setWidgetSession(session);
}
}
}

setInitialized(true);
}

// TODO: Remove this after the widget_setting deployed to all region.
async function initManualSession(
sdk: SendbirdChatWith<[GroupChannelModule]>
) {
Expand All @@ -163,9 +200,9 @@ export const WidgetSettingProvider = ({

const session = {
strategy: sessionStrategy,
expireAt: getDateNDaysLater(30),
userId: injectedUserId,
sessionToken: undefined,
expireAt: getDateNDaysLater(30),
sessionToken: widgetSession?.sessionToken,
channelUrl: channel.url,
};
setWidgetSession(session);
Expand Down
27 changes: 23 additions & 4 deletions src/libs/api/widgetSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ type APIResponse = {
};
};

type Params = {
interface Params extends ConfigureSessionParams {
host: string;
botId: string;
appId: string;
createUserAndChannel: boolean;
}

/**
* This is required to configure the session manually.
* */
type ConfigureSessionParams = {
userId?: string;
sessionKey?: string;
};

type Response = {
Expand All @@ -50,17 +58,21 @@ export async function getWidgetSetting({
botId,
appId,
createUserAndChannel,
userId,
sessionKey,
}: Params): Promise<Response> {
const params = new URLSearchParams({
const headers = sessionKey ? { 'Session-Key': sessionKey } : undefined;
const params = asQueryParams({
create_user_and_channel: createUserAndChannel ? 'True' : 'False',
}).toString();
user_id: createUserAndChannel && userId ? userId : undefined,
});

const path = resolvePath(
host,
`/v3/bots/${botId}/${appId}/widget_setting?${params}`
);

const response = await fetch(path);
const response = await fetch(path, { headers });
const result = await response.json();
if (!response.ok) {
throw new Error(result.message || 'Something went wrong');
Expand Down Expand Up @@ -92,3 +104,10 @@ export async function getWidgetSetting({
: undefined,
};
}

function asQueryParams(obj: object) {
return Object.entries(obj)
.filter(([_, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${key}=${value}`)
.join('&');
}

0 comments on commit e272e38

Please sign in to comment.