mirror of
https://github.com/hexastack/hexabot
synced 2025-04-26 09:19:29 +00:00
fix: widget re-render issues, ws, build + rename pkg
This commit is contained in:
parent
b08ee24fc3
commit
04ce4df348
@ -63,6 +63,10 @@ export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
|
|||||||
blocks: BlockFull[],
|
blocks: BlockFull[],
|
||||||
event: EventWrapper<any, any>,
|
event: EventWrapper<any, any>,
|
||||||
): Promise<BlockFull | undefined> {
|
): Promise<BlockFull | undefined> {
|
||||||
|
if (!blocks.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Search for block matching a given event
|
// Search for block matching a given event
|
||||||
let block: BlockFull | undefined = undefined;
|
let block: BlockFull | undefined = undefined;
|
||||||
const payload = event.getPayload();
|
const payload = event.getPayload();
|
||||||
|
@ -443,7 +443,7 @@ export class BotService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!blocks.length) {
|
if (!blocks.length) {
|
||||||
return this.logger.debug('No starting message blocks was found');
|
this.logger.debug('No starting message blocks was found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for a block match
|
// Search for a block match
|
||||||
|
@ -67,7 +67,7 @@ export const settingModels: SettingCreateDto[] = [
|
|||||||
{
|
{
|
||||||
group: 'nlp_settings',
|
group: 'nlp_settings',
|
||||||
label: 'threshold',
|
label: 'threshold',
|
||||||
value: 0.9,
|
value: 0.1,
|
||||||
type: SettingType.number,
|
type: SettingType.number,
|
||||||
config: {
|
config: {
|
||||||
min: 0,
|
min: 0,
|
||||||
|
@ -68,11 +68,8 @@ describe('WebsocketGateway', () => {
|
|||||||
it('should connect successfully', async () => {
|
it('should connect successfully', async () => {
|
||||||
ioClient.connect();
|
ioClient.connect();
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
// ioClient.on('connect', () => {
|
ioClient.on('connect', () => {
|
||||||
// console.log('connected');
|
expect(true).toBe(true);
|
||||||
// });
|
|
||||||
ioClient.on('message', (data) => {
|
|
||||||
expect(data.statusCode).toBe(200);
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -255,22 +255,6 @@ export class WebsocketGateway
|
|||||||
this.logger.debug(`Number of connected clients: ${sockets?.size}`);
|
this.logger.debug(`Number of connected clients: ${sockets?.size}`);
|
||||||
|
|
||||||
this.eventEmitter.emit(`hook:websocket:connection`, client);
|
this.eventEmitter.emit(`hook:websocket:connection`, client);
|
||||||
// @TODO : Revisit once we don't use anymore in frontend
|
|
||||||
const response = new SocketResponse();
|
|
||||||
client.send(
|
|
||||||
response
|
|
||||||
.setHeaders({
|
|
||||||
'access-control-allow-origin':
|
|
||||||
config.security.cors.allowOrigins.join(','),
|
|
||||||
vary: 'Origin',
|
|
||||||
'access-control-allow-credentials':
|
|
||||||
config.security.cors.allowCredentials.toString(),
|
|
||||||
})
|
|
||||||
.status(200)
|
|
||||||
.json({
|
|
||||||
success: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDisconnect(client: Socket): Promise<void> {
|
async handleDisconnect(client: Socket): Promise<void> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
import withTM from "next-transpile-modules";
|
import withTM from "next-transpile-modules";
|
||||||
|
|
||||||
const nextConfig = withTM(["hexabot-widget"])({
|
const nextConfig = withTM(["hexabot-chat-widget"])({
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -11,7 +11,7 @@ const nextConfig = withTM(["hexabot-widget"])({
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
webpack(config) {
|
webpack(config) {
|
||||||
if (process.env.NODE_ENV==="development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
config.watchOptions = {
|
config.watchOptions = {
|
||||||
poll: 1000,
|
poll: 1000,
|
||||||
aggregateTimeout: 300,
|
aggregateTimeout: 300,
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"eazychart-css": "^0.2.1-alpha.0",
|
"eazychart-css": "^0.2.1-alpha.0",
|
||||||
"eazychart-react": "^0.8.0-alpha.0",
|
"eazychart-react": "^0.8.0-alpha.0",
|
||||||
"hexabot-widget": "*",
|
"hexabot-chat-widget": "*",
|
||||||
"next": "^14.2.13",
|
"next": "^14.2.13",
|
||||||
"next-transpile-modules": "^10.0.1",
|
"next-transpile-modules": "^10.0.1",
|
||||||
"normalizr": "^3.6.2",
|
"normalizr": "^3.6.2",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Avatar, Box } from "@mui/material";
|
import { Avatar, Box } from "@mui/material";
|
||||||
import UiChatWidget from "hexabot-widget/src/UiChatWidget";
|
import UiChatWidget from "hexabot-chat-widget/src/UiChatWidget";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
import { getAvatarSrc } from "@/components/inbox/helpers/mapMessages";
|
import { getAvatarSrc } from "@/components/inbox/helpers/mapMessages";
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -63,7 +63,7 @@
|
|||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"eazychart-css": "^0.2.1-alpha.0",
|
"eazychart-css": "^0.2.1-alpha.0",
|
||||||
"eazychart-react": "^0.8.0-alpha.0",
|
"eazychart-react": "^0.8.0-alpha.0",
|
||||||
"hexabot-widget": "*",
|
"hexabot-chat-widget": "*",
|
||||||
"next": "^14.2.13",
|
"next": "^14.2.13",
|
||||||
"next-transpile-modules": "^10.0.1",
|
"next-transpile-modules": "^10.0.1",
|
||||||
"normalizr": "^3.6.2",
|
"normalizr": "^3.6.2",
|
||||||
@ -6356,6 +6356,10 @@
|
|||||||
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.5.tgz",
|
||||||
"integrity": "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg=="
|
"integrity": "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/hexabot-chat-widget": {
|
||||||
|
"resolved": "widget",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/hexabot-cli": {
|
"node_modules/hexabot-cli": {
|
||||||
"resolved": "cli",
|
"resolved": "cli",
|
||||||
"link": true
|
"link": true
|
||||||
@ -6364,10 +6368,6 @@
|
|||||||
"resolved": "frontend",
|
"resolved": "frontend",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/hexabot-widget": {
|
|
||||||
"resolved": "widget",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/hoist-non-react-statics": {
|
"node_modules/hoist-non-react-statics": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
@ -9918,7 +9918,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widget": {
|
"widget": {
|
||||||
"name": "hexabot-widget",
|
"name": "hexabot-chat-widget",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -104,6 +104,17 @@ To prevent the website css from conflicting with the chat widget css, we can lev
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you would like to use the official widget and benefit from updates automatically, you can consider using the cdn url:
|
||||||
|
`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2.0.4/dist/`
|
||||||
|
|
||||||
|
or lastest from major version:
|
||||||
|
`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2/dist/`
|
||||||
|
|
||||||
|
JsDelivr uses the package published in the NPM registry : https://www.npmjs.com/package/hexabot-widget
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
As a proof of concept we developed a Wordpress plugin to embed the chat widget in a Wordpress website : [https://github.com/hexastack/hexabot-wordpress-live-chat-widget](https://github.com/hexastack/hexabot-wordpress-live-chat-widget)
|
||||||
|
|
||||||
## Customization
|
## Customization
|
||||||
You can customize the look and feel of the chat widget by modifying the widget’s scss styles or behavior. The widget allows you to:
|
You can customize the look and feel of the chat widget by modifying the widget’s scss styles or behavior. The widget allows you to:
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexabot-live-chat-widget",
|
"name": "hexabot-chat-widget",
|
||||||
"version": "2.0.0-rc.2",
|
"version": "2.0.0",
|
||||||
"description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
|
"description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
|
||||||
"author": "Hexastack",
|
"author": "Hexastack",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
|
@ -15,7 +15,6 @@ import { useChat } from '../providers/ChatProvider';
|
|||||||
import { useColors } from '../providers/ColorProvider';
|
import { useColors } from '../providers/ColorProvider';
|
||||||
import { useSocketLifecycle } from '../providers/SocketProvider';
|
import { useSocketLifecycle } from '../providers/SocketProvider';
|
||||||
import { useWidget, WidgetContextType } from '../providers/WidgetProvider';
|
import { useWidget, WidgetContextType } from '../providers/WidgetProvider';
|
||||||
|
|
||||||
import './Launcher.scss';
|
import './Launcher.scss';
|
||||||
|
|
||||||
type LauncherProps = PropsWithChildren<{
|
type LauncherProps = PropsWithChildren<{
|
||||||
|
@ -6,7 +6,13 @@
|
|||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';
|
import React, {
|
||||||
|
SyntheticEvent,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { useTranslation } from '../hooks/useTranslation';
|
import { useTranslation } from '../hooks/useTranslation';
|
||||||
import { useChat } from '../providers/ChatProvider';
|
import { useChat } from '../providers/ChatProvider';
|
||||||
@ -39,6 +45,7 @@ const UserSubscription: React.FC = () => {
|
|||||||
} = useChat();
|
} = useChat();
|
||||||
const [firstName, setFirstName] = useState<string>('');
|
const [firstName, setFirstName] = useState<string>('');
|
||||||
const [lastName, setLastName] = useState<string>('');
|
const [lastName, setLastName] = useState<string>('');
|
||||||
|
const isInitialized = useRef(false);
|
||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
async (event?: React.FormEvent<HTMLFormElement>) => {
|
async (event?: React.FormEvent<HTMLFormElement>) => {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
@ -83,7 +90,7 @@ const UserSubscription: React.FC = () => {
|
|||||||
data: {
|
data: {
|
||||||
type: TOutgoingMessageType.postback,
|
type: TOutgoingMessageType.postback,
|
||||||
data: {
|
data: {
|
||||||
text: 'GET_STARTED', //TODO:use translation here?
|
text: t('messages.get_started'),
|
||||||
payload: 'GET_STARTED',
|
payload: 'GET_STARTED',
|
||||||
},
|
},
|
||||||
author: profile.foreign_id,
|
author: profile.foreign_id,
|
||||||
@ -113,14 +120,18 @@ const UserSubscription: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const profile = localStorage.getItem('profile');
|
// User already subscribed ? (example : refreshed the page)
|
||||||
|
if (!isInitialized.current) {
|
||||||
|
isInitialized.current = true;
|
||||||
|
const profile = localStorage.getItem('profile');
|
||||||
|
|
||||||
if (profile) {
|
if (profile) {
|
||||||
const parsedProfile = JSON.parse(profile);
|
const parsedProfile = JSON.parse(profile);
|
||||||
|
|
||||||
setFirstName(parsedProfile.first_name);
|
setFirstName(parsedProfile.first_name);
|
||||||
setLastName(parsedProfile.last_name);
|
setLastName(parsedProfile.last_name);
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [handleSubmit, setScreen]);
|
}, [handleSubmit, setScreen]);
|
||||||
|
|
||||||
|
@ -241,15 +241,14 @@ const ChatProvider: React.FC<{
|
|||||||
) {
|
) {
|
||||||
if ('author' in newIOMessage) {
|
if ('author' in newIOMessage) {
|
||||||
newIOMessage.direction =
|
newIOMessage.direction =
|
||||||
newIOMessage.author === participants[1].foreign_id ||
|
newIOMessage.author === 'chatbot'
|
||||||
newIOMessage.author === participants[1].id
|
? Direction.received
|
||||||
? Direction.sent
|
: Direction.sent;
|
||||||
: Direction.received;
|
|
||||||
newIOMessage.read = true;
|
newIOMessage.read = true;
|
||||||
newIOMessage.delivery = true;
|
newIOMessage.delivery = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.push(newIOMessage as TMessage);
|
setMessages([...messages, newIOMessage as TMessage]);
|
||||||
setScroll(0);
|
setScroll(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,30 +309,29 @@ const ChatProvider: React.FC<{
|
|||||||
}>(
|
}>(
|
||||||
`/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`,
|
`/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`,
|
||||||
);
|
);
|
||||||
const { messages, profile } = body;
|
|
||||||
|
|
||||||
localStorage.setItem('profile', JSON.stringify(profile));
|
localStorage.setItem('profile', JSON.stringify(body.profile));
|
||||||
// @TODO : condition mix on id VS foreign_id
|
setMessages(
|
||||||
messages.forEach((message) => {
|
body.messages.map((message) => {
|
||||||
const direction =
|
return {
|
||||||
message.author === profile.foreign_id ||
|
...message,
|
||||||
message.author === profile.id
|
direction:
|
||||||
? Direction.sent
|
message.author === body.profile.foreign_id ||
|
||||||
: Direction.received;
|
message.author === body.profile.id
|
||||||
|
? Direction.sent
|
||||||
message.direction = direction;
|
: Direction.received,
|
||||||
if (message.direction === Direction.sent) {
|
read: message.direction === Direction.sent || message.read,
|
||||||
message.read = true;
|
delivery:
|
||||||
message.delivery = false;
|
message.direction === Direction.sent || message.delivery,
|
||||||
}
|
} as TMessage;
|
||||||
});
|
}),
|
||||||
setMessages(messages);
|
);
|
||||||
setParticipants([
|
setParticipants([
|
||||||
...participants,
|
...participants,
|
||||||
{
|
{
|
||||||
id: profile.foreign_id,
|
id: body.profile.foreign_id,
|
||||||
foreign_id: profile.foreign_id,
|
foreign_id: body.profile.foreign_id,
|
||||||
name: `${profile.first_name} ${profile.last_name}`,
|
name: `${body.profile.first_name} ${body.profile.last_name}`,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
setConnectionState(3);
|
setConnectionState(3);
|
||||||
|
@ -12,43 +12,42 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { useConfig } from './ConfigProvider';
|
import { useConfig } from './ConfigProvider';
|
||||||
import { builSocketIoClient, SocketIoClient } from '../utils/SocketIoClient';
|
import { getSocketIoClient, SocketIoClient } from '../utils/SocketIoClient';
|
||||||
|
|
||||||
interface socketContext {
|
interface socketContext {
|
||||||
socket: SocketIoClient;
|
socket: SocketIoClient;
|
||||||
connected: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketContext = createContext<socketContext>({
|
const socketContext = createContext<socketContext>({
|
||||||
socket: {} as SocketIoClient,
|
socket: {} as SocketIoClient,
|
||||||
connected: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SocketProvider = (props: PropsWithChildren) => {
|
export const SocketProvider = (props: PropsWithChildren) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const socketRef = useRef(builSocketIoClient(config));
|
const socketRef = useRef(
|
||||||
const [connected, setConnected] = useState(false);
|
getSocketIoClient(config, {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
socketRef.current.init({
|
|
||||||
onConnect: () => {
|
onConnect: () => {
|
||||||
setConnected(true);
|
// eslint-disable-next-line no-console
|
||||||
|
console.info(
|
||||||
|
'Hexabot Live Chat : Successfully established WS Connection!',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onConnectError: () => {
|
onConnectError: () => {
|
||||||
setConnected(false);
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Hexabot Live Chat : Failed to establish WS Connection!');
|
||||||
},
|
},
|
||||||
onDisconnect: () => {
|
onDisconnect: () => {
|
||||||
setConnected(false);
|
// eslint-disable-next-line no-console
|
||||||
|
console.info('Hexabot Live Chat : Disconnected WS.');
|
||||||
},
|
},
|
||||||
});
|
}),
|
||||||
}, []);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<socketContext.Provider value={{ socket: socketRef.current, connected }}>
|
<socketContext.Provider value={{ socket: socketRef.current }}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</socketContext.Provider>
|
</socketContext.Provider>
|
||||||
);
|
);
|
||||||
@ -58,12 +57,6 @@ export const useSocket = () => {
|
|||||||
return useContext(socketContext);
|
return useContext(socketContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSocketConnected = () => {
|
|
||||||
const { connected } = useSocket();
|
|
||||||
|
|
||||||
return connected;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSubscribe = <T,>(event: string, callback: (arg: T) => void) => {
|
export const useSubscribe = <T,>(event: string, callback: (arg: T) => void) => {
|
||||||
const { socket } = useSocket();
|
const { socket } = useSocket();
|
||||||
|
|
||||||
@ -83,5 +76,6 @@ export const useSocketLifecycle = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
};
|
};
|
||||||
}, [socket]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"back": "Back"
|
"back": "Back"
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"get_started": "Get Started",
|
||||||
"file_message": {
|
"file_message": {
|
||||||
"browser_audio_unsupport": "Browser does not support the audio element.",
|
"browser_audio_unsupport": "Browser does not support the audio element.",
|
||||||
"browser_video_unsupport": "Browser does not support the video element.",
|
"browser_video_unsupport": "Browser does not support the video element.",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"back": "Retour"
|
"back": "Retour"
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"get_started": "Démarrer",
|
||||||
"file_message": {
|
"file_message": {
|
||||||
"browser_audio_unsupport": "Le navigateur ne prend pas en charge l'élément audio.",
|
"browser_audio_unsupport": "Le navigateur ne prend pas en charge l'élément audio.",
|
||||||
"browser_video_unsupport": "Le navigateur ne prend pas en charge l'élément vidéo.",
|
"browser_video_unsupport": "Le navigateur ne prend pas en charge l'élément vidéo.",
|
||||||
|
@ -16,6 +16,12 @@ import {
|
|||||||
|
|
||||||
type SocketIoClientConfig = Partial<ManagerOptions & SocketOptions>;
|
type SocketIoClientConfig = Partial<ManagerOptions & SocketOptions>;
|
||||||
|
|
||||||
|
type SocketIoEventHandlers = {
|
||||||
|
onConnect?: () => void;
|
||||||
|
onDisconnect?: (reason: string, details: unknown) => void;
|
||||||
|
onConnectError?: (error: Error) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export class SocketIoClient {
|
export class SocketIoClient {
|
||||||
/**
|
/**
|
||||||
* Default configuration for the socket client
|
* Default configuration for the socket client
|
||||||
@ -50,9 +56,11 @@ export class SocketIoClient {
|
|||||||
|
|
||||||
private config: SocketIoClientConfig;
|
private config: SocketIoClientConfig;
|
||||||
|
|
||||||
private initialized: boolean = false;
|
constructor(
|
||||||
|
apiUrl: string,
|
||||||
constructor(apiUrl: string, socketConfig?: SocketIoClientConfig) {
|
socketConfig: SocketIoClientConfig,
|
||||||
|
handlers: SocketIoEventHandlers,
|
||||||
|
) {
|
||||||
this.config = {
|
this.config = {
|
||||||
...SocketIoClient.defaultConfig,
|
...SocketIoClient.defaultConfig,
|
||||||
...socketConfig,
|
...socketConfig,
|
||||||
@ -61,6 +69,7 @@ export class SocketIoClient {
|
|||||||
const url = new URL(apiUrl);
|
const url = new URL(apiUrl);
|
||||||
|
|
||||||
this.socket = io(url.origin, this.config);
|
this.socket = io(url.origin, this.config);
|
||||||
|
this.init(handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,16 +80,10 @@ export class SocketIoClient {
|
|||||||
onConnect,
|
onConnect,
|
||||||
onDisconnect,
|
onDisconnect,
|
||||||
onConnectError,
|
onConnectError,
|
||||||
}: {
|
}: SocketIoEventHandlers) {
|
||||||
onConnect?: () => void;
|
|
||||||
onDisconnect?: (reason: string, details: unknown) => void;
|
|
||||||
onConnectError?: (error: Error) => void;
|
|
||||||
}) {
|
|
||||||
if (!this.initialized) this.socket.connect();
|
|
||||||
onConnect && this.uniqueOn('connect', onConnect);
|
onConnect && this.uniqueOn('connect', onConnect);
|
||||||
onDisconnect && this.uniqueOn('disconnect', onDisconnect);
|
onDisconnect && this.uniqueOn('disconnect', onDisconnect);
|
||||||
onConnectError && this.uniqueOn('connect_error', onConnectError);
|
onConnectError && this.uniqueOn('connect_error', onConnectError);
|
||||||
this.initialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +103,6 @@ export class SocketIoClient {
|
|||||||
*/
|
*/
|
||||||
public disconnect() {
|
public disconnect() {
|
||||||
this.socket.disconnect();
|
this.socket.disconnect();
|
||||||
this.initialized = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,10 +186,28 @@ export class SocketIoClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const builSocketIoClient = (config: Config) =>
|
let socketIoClient: SocketIoClient;
|
||||||
new SocketIoClient(config.apiUrl, {
|
|
||||||
query: {
|
/**
|
||||||
channel: config.channel,
|
* Returns a singleton instance of the socket io client
|
||||||
verification_token: config.token,
|
*
|
||||||
},
|
* @param config The socket connection config
|
||||||
});
|
* @param handlers Event handlers
|
||||||
|
* @returns Socket io client instance
|
||||||
|
*/
|
||||||
|
export const getSocketIoClient = (config: Config, handlers: SocketIoEventHandlers) => {
|
||||||
|
if (!socketIoClient) {
|
||||||
|
socketIoClient = new SocketIoClient(
|
||||||
|
config.apiUrl,
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
channel: config.channel,
|
||||||
|
verification_token: config.token,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return socketIoClient;
|
||||||
|
};
|
||||||
|
@ -5,28 +5,31 @@ import { defineConfig } from "vite";
|
|||||||
import dts from "vite-plugin-dts";
|
import dts from "vite-plugin-dts";
|
||||||
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => {
|
||||||
plugins: [react(), dts()],
|
return {
|
||||||
server: {
|
plugins: [react(), dts()],
|
||||||
host: "0.0.0.0",
|
server: {
|
||||||
},
|
host: '0.0.0.0',
|
||||||
define: {
|
|
||||||
"process.env": process.env,
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: resolve(__dirname, "src/ChatWidget.tsx"),
|
|
||||||
name: "HexabotWidget",
|
|
||||||
fileName: (format) => `hexabot-widget.${format}.js`,
|
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
define: {
|
||||||
external: ["react", "react-dom"],
|
'process.env':
|
||||||
output: {
|
mode === 'development' ? { 'process.env': process.env } : {},
|
||||||
globals: {
|
},
|
||||||
react: "React",
|
build: {
|
||||||
"react-dom": "ReactDOM",
|
lib: {
|
||||||
|
entry: resolve(__dirname, 'src/ChatWidget.tsx'),
|
||||||
|
name: 'HexabotWidget',
|
||||||
|
fileName: (format) => `hexabot-widget.${format}.js`,
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['react', 'react-dom'],
|
||||||
|
output: {
|
||||||
|
globals: {
|
||||||
|
react: 'React',
|
||||||
|
'react-dom': 'ReactDOM',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user