Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 4 additions & 5 deletions .storybook/decorators/TockContextDecorator.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';

import TockProvider from '../../src/TockContext';
import { JSX } from 'react';
import TockContext from '../../src/TockContext';

const TockContextDecorator = (Story: () => JSX.Element) => (
<TockProvider>
<TockContext endpoint="">
<Story/>
</TockProvider>
</TockContext>
);

export default TockContextDecorator;
51 changes: 34 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,20 @@ Your bundler must support ESM modules, which is the case for Webpack, Rollup and
import { ThemeProvider } from "@emotion/react";
import { TockContext, Chat, createTheme } from 'tock-react-kit';

<TockContext settings={{
endPoint: "<TOCK_BOT_API_URL>",
<TockContext endpoint="<TOCK_BOT_API_URL>" settings={{
// all properties from TockSettings can be configured here, like:
locale: navigator.language,
network: {
disableSse: true,
},
}}>
<ThemeProvider theme={createTheme({ /* ... */})}>
<Chat
/* The following parameters are optional */
referralParameter="referralParameter"
// also accepts all properties from TockOptions, like:
disableSse
/* All parameters are optional */
afterInit={() => {}}
openingMessage="Hi"
referralParameter="referralParameter"
widgets={{...}}
/>
</ThemeProvider>
</TockContext>
Expand Down Expand Up @@ -217,10 +221,12 @@ Renders an entire chat in a target element.
| `theme` | `TockTheme` | Optional theme object |
| `options` | `TockOptions` | Optional options object |

### `useTock(tockBotApiUrl, extraHeadersProvider, disableSse, localStorageHistory)`
### `useTock()`

Hook that provides chat history and methods to communicate with the Tock Bot. It must be used alongside with `TockContext`. Returns a useTock interface.

Until version 24.9.0 of the tock-react-kit, this function took the following arguments, which are now moved to [`TockSettings`](#TockSettings):

| Argument name | Type | Description |
|------------------------|-----------------------------------------|---------------------------------------------------------------|
| `tockBotApiUrl` | `string` | URL to the Tock Bot REST API |
Expand Down Expand Up @@ -334,18 +340,30 @@ A `TockTheme` can be used as a value of a `ThemeProvider` of [`emotion-theming`]

### `TockSettings`

The main source of configuration for the chatbot interface.
Objects implementing this interface can be passed to `renderChat` or to `TockContext`.

| Property name | Type | Description |
|----------------|-------------------------|------------------------------------------------------|
| `endPoint` | `string` | URL for the bot's web connector endpoint |
| `locale` | `string?` | Optional user language, as an *RFC 5646* code |
| `localStorage` | `LocalStorageSettings?` | Configuration for use of localStorage by the library |
| `network` | `NetworkSettings?` | If `true`, disables any SSE connection attempt |
| `renderers` | `RendererSettings?` | Configuration for custom image and text renderers |

#### `LocalStorageSettings`

| Property name | Type | Description |
|-----------------|-----------|-----------------------------------------------------------------------------------------------|
| `storagePrefix` | `string?` | Prefix for local storage keys allowing communication with different bots from the same domain |
| Property name | Type | Description |
|------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `enableMessageHistory` | `boolean?` | If set to `true`, the most recent messages of a conversation will be persisted in the local storage. Defaults to `false`. |
| `maxMessageCount` | `number?` | When message history is enabled, sets the max number of messages to store. Defaults to 10. |
| `prefix` | `string?` | Prefix for local storage keys allowing communication with different bots from the same domain (used for both `userId` and message history). |

#### `NetworkSettings`

| Property name | type | Description |
|------------------------|------------------------------------------|---------------------------------------------------|
| `disableSse` | `boolean?` | If `true`, disables any SSE connection attempt |
| `extraHeadersProvider` | `() => Promise<Record<string, string>>?` | Provides extra HTTP headers for outgoing requests |

#### `RendererSettings`

Expand Down Expand Up @@ -489,10 +507,9 @@ renderChat(

#### Local storage history

The optional `localStorageHistory` makes it possible to provide a history session of messages.
The optional `localStorage.enableMessageHistory` setting (disabled by default) makes it possible to provide a history session of messages.
This history loads at the creation of the chat and is stored in the local storage of the browser.

The `localStorageHistory` parameter is an object, by default not set (enable then to false).
The number of persisted messages can be configured with the `localStorage.maxMessageCount` setting.

Example:

Expand All @@ -502,9 +519,9 @@ renderChat(
'<TOCK_BOT_API_URL>',
undefined,
{},
{ localStorageHistory: {
enable: true,
maxNumberMessages: 15, // by default 10 messages max
{ localStorage: {
enableMessageHistory: true,
maxMessageCount: 15, // default is 10 messages max
}
},
);
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"release": "np"
},
"dependencies": {
"@microsoft/fetch-event-source": "^2.0.1",
"deepmerge": "^4.2.2",
"linkify-html": "^3.0.5",
"linkifyjs": "^3.0.5",
Expand Down Expand Up @@ -95,5 +96,6 @@
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
63 changes: 0 additions & 63 deletions src/Sse.ts

This file was deleted.

171 changes: 25 additions & 146 deletions src/TockContext.tsx
Original file line number Diff line number Diff line change
@@ -1,158 +1,31 @@
import {
Context,
createContext,
Dispatch,
ReactNode,
Reducer,
useContext,
useReducer,
} from 'react';
import { JSX, ReactNode, useReducer } from 'react';
import deepmerge from 'deepmerge';
import { PartialDeep } from 'type-fest';
import { retrieveUserId } from './utils';
import { QuickReply } from './model/buttons';
import { Message } from './model/messages';
import TockSettings, { defaultSettings } from './settings/TockSettings';

export const TockSettingsContext: Context<TockSettings | undefined> =
createContext<TockSettings | undefined>(undefined);

export const TockStateContext: Context<TockState | undefined> = createContext<
TockState | undefined
>(undefined);
export const TockStateDispatch: Context<Dispatch<TockAction> | undefined> =
createContext<Dispatch<TockAction> | undefined>(undefined);

export const useTockSettings: () => TockSettings = () => {
const settings = useContext(TockSettingsContext);
if (!settings) {
throw new Error('useTockSettings must be used in a TockContext');
}
return settings;
};

export const useTockState: () => TockState = () => {
const state: TockState | undefined = useContext(TockStateContext);
if (!state) {
throw new Error('useTockState must be used in a TockContext');
}
return state;
};

export const useTockDispatch: () => Dispatch<TockAction> = () => {
const dispatch: Dispatch<TockAction> | undefined =
useContext(TockStateDispatch);
if (!dispatch) {
throw new Error('useTockDispatch must be used in a TockContext');
}
return dispatch;
};

export interface TockState {
quickReplies: QuickReply[];
messages: Message[];
userId: string;
loading: boolean;
sseInitializing: boolean;
metadata: Record<string, string>;
error: boolean;
}

export interface TockAction {
type:
| 'SET_QUICKREPLIES'
| 'ADD_MESSAGE'
| 'SET_METADATA'
| 'SET_LOADING'
| 'SET_SSE_INITIALIZING'
| 'CLEAR_MESSAGES'
| 'SET_ERROR';
quickReplies?: QuickReply[];
messages?: Message[];
loading?: boolean;
sseInitializing?: boolean;
metadata?: Record<string, string>;
error?: boolean;
}

const tockReducer: Reducer<TockState, TockAction> = (
state: TockState,
action: TockAction,
): TockState => {
switch (action.type) {
case 'SET_QUICKREPLIES':
if (action.quickReplies) {
return {
...state,
quickReplies: action.quickReplies,
error: !!action.error,
};
}
break;
case 'ADD_MESSAGE':
if (action.messages) {
return {
...state,
messages: [...state.messages, ...action.messages],
error: !!action.error,
};
}
break;
case 'SET_LOADING':
if (action.loading != undefined) {
return {
...state,
loading: action.loading,
};
}
break;
case 'SET_SSE_INITIALIZING':
if (action.sseInitializing != undefined) {
return {
...state,
sseInitializing: action.sseInitializing,
error: !!action.error,
};
}
break;
case 'CLEAR_MESSAGES':
if (state.messages) {
return {
...state,
messages: [],
};
}
break;
case 'SET_METADATA':
if (action.metadata != undefined) {
return {
...state,
metadata: action.metadata,
};
}
break;
case 'SET_ERROR':
return {
...state,
error: !!action.error,
};
default:
break;
}
return state;
};
import TockSettings, {
defaultSettings,
TockOptionalSettings,
} from './settings/TockSettings';
import { TockNetworkContext } from './network/TockNetworkContext';
import { tockReducer, TockStateContext, TockStateDispatch } from './TockState';
import { TockSettingsContext } from './settings/TockSettingsContext';

const TockContext: (props: {
children?: ReactNode;
settings?: PartialDeep<TockSettings>;
endpoint?: string; // will be required in a future release
settings?: TockOptionalSettings;
}) => JSX.Element = ({
children,
endpoint,
settings = {},
}: {
children?: ReactNode;
settings: PartialDeep<TockSettings>;
}) => {
const mergedSettings = deepmerge(defaultSettings, settings) as TockSettings;
endpoint?: string;
settings?: TockOptionalSettings;
}): JSX.Element => {
const mergedSettings = deepmerge(defaultSettings, {
endpoint,
...settings,
}) as TockSettings;
const [state, dispatch] = useReducer(tockReducer, {
quickReplies: [],
messages: [],
Expand All @@ -166,7 +39,13 @@ const TockContext: (props: {
<TockSettingsContext.Provider value={mergedSettings}>
<TockStateContext.Provider value={state}>
<TockStateDispatch.Provider value={dispatch}>
{children}
{endpoint ? (
<TockNetworkContext endpoint={endpoint} settings={mergedSettings}>
{children}
</TockNetworkContext>
) : (
children
)}
</TockStateDispatch.Provider>
</TockStateContext.Provider>
</TockSettingsContext.Provider>
Expand Down
3 changes: 3 additions & 0 deletions src/TockLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @deprecated configure local storage in TockSettings instead
*/
export interface TockLocalStorage {
enable?: boolean;
maxNumberMessages?: number;
Expand Down
Loading