Merge branch 'main' into 406-bug-chat-console-settings-applied-without-refreshing-the-page

This commit is contained in:
yassinedorbozgithub
2024-12-05 12:07:37 +01:00
91 changed files with 1376 additions and 1355 deletions

View File

@@ -9,6 +9,7 @@
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { BaseSchema } from '@/utils/generics/base-schema';
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
import { THydratedDocument } from '@/utils/types/filter.types';
export enum BotStatsType {
@@ -115,9 +116,9 @@ export class BotStats extends BaseSchema {
export type BotStatsDocument = THydratedDocument<BotStats>;
export const BotStatsModel: ModelDefinition = {
export const BotStatsModel: ModelDefinition = LifecycleHookManager.attach({
name: BotStats.name,
schema: SchemaFactory.createForClass(BotStats),
};
});
export default BotStatsModel.schema;

View File

@@ -11,6 +11,7 @@ import { Transform, Type } from 'class-transformer';
import { Schema as MongooseSchema } from 'mongoose';
import { BaseSchema } from '@/utils/generics/base-schema';
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
import {
TFilterPopulateFields,
THydratedDocument,
@@ -103,10 +104,11 @@ export class NlpSampleEntityFull extends NlpSampleEntityStub {
export type NlpSampleEntityDocument = THydratedDocument<NlpSampleEntity>;
export const NlpSampleEntityModel: ModelDefinition = {
name: NlpSampleEntity.name,
schema: SchemaFactory.createForClass(NlpSampleEntityStub),
};
export const NlpSampleEntityModel: ModelDefinition =
LifecycleHookManager.attach({
name: NlpSampleEntity.name,
schema: SchemaFactory.createForClass(NlpSampleEntityStub),
});
export default NlpSampleEntityModel.schema;

View File

@@ -9,23 +9,23 @@
import { type IconDefinition } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
List,
Theme,
styled,
Divider,
Collapse,
Divider,
List,
ListItemButton,
ListItemIcon,
ListItemText,
ListSubheader,
ListItemButton,
SvgIconTypeMap,
ListItemIcon,
Tooltip as MuiTooltip,
SvgIconTypeMap,
Theme,
TooltipProps,
styled,
} from "@mui/material";
// @icon
import { OverridableComponent } from "@mui/material/OverridableComponent";
import Link from "next/link";
import { useState, useEffect } from "react";
import { useEffect, useState } from "react";
import { useTranslate } from "@/hooks/useTranslate";
import { TTranslationKeys } from "@/i18n/i18n.types";

View File

@@ -205,6 +205,8 @@ const getMenuItems = (ssoEnabled: boolean): MenuItem[] => [
[EntityType.LABEL]: [PermissionAction.READ],
},
},
// {
// text: 'menu.broadcast',
// href: "/subscribers/broadcast",

View File

@@ -1,16 +1,15 @@
{
"plugins": [
"@typescript-eslint/eslint-plugin",
"import",
"react",
"license-header"
],
"root": true,
"plugins": ["@typescript-eslint", "import", "license-header", "react"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"rules": {
"react/jsx-no-target-blank": ["off"],
"react/react-in-jsx-scope": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
@@ -24,19 +23,17 @@
"error",
{
"groups": [
"builtin", // Built-in imports (come from NodeJS native) go first
"external", // <- External imports
"unknown", // <- unknown
"index", // <- index imports
"internal", // <- Absolute imports
"parent", // <- Relative imports, the sibling and parent types they can be mingled together
"builtin",
"external",
"unknown",
"index",
"internal",
"parent",
"sibling"
],
"newlines-between": "always",
"alphabetize": {
/* sort in ascending order. Options: ["ignore", "asc", "desc"] */
"order": "asc",
/* ignore case. Options: [true, false] */
"caseInsensitive": true
}
}
@@ -50,8 +47,8 @@
"error",
{ "blankLine": "never", "prev": ["const"], "next": "const" }
],
"react/jsx-curly-brace-presence": "warn",
"react/self-closing-comp": "error",
"react/jsx-curly-brace-presence": ["warn"],
"react/self-closing-comp": ["error"],
"license-header/header": [
"error",
[

4
widget/.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"singleQuote": false,
"trailingComma": "all"
}

View File

@@ -1,15 +1,18 @@
# Hexabot Live Chat Widget
The [Hexabot](https://hexabot.ai/) Live Chat Widget is a React-based embeddable widget that allows users to integrate real-time chat functionality into their websites. It connects to the Hexabot API and facilitates seamless interaction between end-users and chatbots across multiple channels.
[Hexabot](https://hexabot.ai/) is an open-source chatbot / agent solution that allows users to create and manage AI-powered, multi-channel, and multilingual chatbots with ease. If you would like to learn more, please visit the [official github repo](https://github.com/Hexastack/Hexabot/).
## Key Features
- **Real-Time Chat:** Engage in real-time conversations with users directly through your website.
- **Customizable:** Easily customize the widget's appearance and behavior to fit your brand and website.
- **Multi-Channel Support:** Integrates with multiple messaging platforms through the Hexabot API.
- **Embeddable:** Simple to embed and integrate into any web page with just a few lines of code.
## Directory Structure
The Hexabot Live Chat Widget is organized into the following directory structure, under `src` we have:
- **src/assets:** Static assets like icons, fonts, and images used in the widget.
@@ -23,30 +26,33 @@ The Hexabot Live Chat Widget is organized into the following directory structure
- **src/types:** Defines the typescript interfaces, types, and enums used.
- **src/utils:** Utility functions and helpers used throughout the widget, such as formatting, validations, or data transformations.
- **/public:** Contains static files that are publicly accessible. This includes the main HTML template where the widget is embedded for local development.
## Run the Live Chat Widget
### Dev Mode
To run the widget in development mode, execute the following command at the project root level:
```bash
npm run dev:widget
```
The live chat widget will be accessible at http://localhost:5173.
### Build for Production
To build the widget for production, execute the following command at the widget folder level:
```bash
npm run build
```
This will generate a production-ready build in the dist folder.
## Embed Chat Widget
Once the widget is built, you can easily embed it into any webpage. Here's an example of how to add it to your website:
Once the widget is built, you can easily embed it into any webpage. Here's an example of how to add it to your website:
```js
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
@@ -68,9 +74,11 @@ Once the widget is built, you can easily embed it into any webpage. Here's an ex
);
</script>
```
Replace the values in apiUrl and token with your configuration details.
To prevent the website css from conflicting with the chat widget css, we can leverage the shadow dom:
To prevent the website css from conflicting with the chat widget css, we can leverage the shadow dom:
```js
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
@@ -84,7 +92,7 @@ To prevent the website css from conflicting with the chat widget css, we can lev
document
.getElementById('hb-chat-widget')
.attachShadow({ mode: 'open' })
.append(
.append(
shadowContainer,
createElement("link", {
rel: "stylesheet",
@@ -104,7 +112,7 @@ To prevent the website css from conflicting with the chat widget css, we can lev
</script>
```
If you would like to use the official widget and benefit from updates automatically, you can consider using the cdn url:
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:
@@ -113,20 +121,24 @@ or lastest from major version:
JsDelivr uses the package published in the NPM registry : https://www.npmjs.com/package/hexabot-chat-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
You can customize the look and feel of the chat widget by modifying the widgets scss styles or behavior. The widget allows you to:
- Change colors and fonts to match your website's branding.
- Configure user settings like language and chatbot response preferences.
## Contributing
## Contributing
We welcome contributions from the community! Whether you want to report a bug, suggest new features, or submit a pull request, your input is valuable to us.
Feel free to join us on [Discord](https://discord.gg/rNb9t2MFkG)
## License
This software is licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.

View File

@@ -34,6 +34,7 @@
"@typescript-eslint/parser": "^7.15.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"sass": "^1.77.8",

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -19,23 +19,24 @@
<div id="hb-chat-widget"></div>
<script>
// Create the shadow root and attach it to the widget container
const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props);
const createElement = (tag, props = {}) =>
Object.assign(document.createElement(tag), props);
const shadowContainer = createElement("div");
document
.getElementById('hb-chat-widget')
.attachShadow({ mode: 'open' })
.append(
shadowContainer,
createElement("link", {
rel: "stylesheet",
href: "./style.css"
})
);
.getElementById("hb-chat-widget")
.attachShadow({ mode: "open" })
.append(
shadowContainer,
createElement("link", {
rel: "stylesheet",
href: "./style.css",
}),
);
ReactDOM.render(
React.createElement(HexabotWidget, {
apiUrl: 'http://localhost:4000',
channel: 'web-channel',
token: 'token123',
apiUrl: "http://localhost:4000",
channel: "web-channel",
token: "token123",
}),
shadowContainer,
);

View File

@@ -1,3 +1,2 @@
#root {
}

View File

@@ -6,18 +6,18 @@
* 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 Launcher from './components/Launcher';
import UserSubscription from './components/UserSubscription';
import ChatProvider from './providers/ChatProvider';
import { ColorProvider } from './providers/ColorProvider';
import { Config, ConfigProvider } from './providers/ConfigProvider';
import { CookieProvider } from './providers/CookieProvider';
import { SettingsProvider } from './providers/SettingsProvider';
import { SocketProvider } from './providers/SocketProvider';
import { TranslationProvider } from './providers/TranslationProvider';
import WidgetProvider from './providers/WidgetProvider';
import 'normalize.css';
import './ChatWidget.css';
import Launcher from "./components/Launcher";
import UserSubscription from "./components/UserSubscription";
import ChatProvider from "./providers/ChatProvider";
import { ColorProvider } from "./providers/ColorProvider";
import { Config, ConfigProvider } from "./providers/ConfigProvider";
import { CookieProvider } from "./providers/CookieProvider";
import { SettingsProvider } from "./providers/SettingsProvider";
import { SocketProvider } from "./providers/SocketProvider";
import { TranslationProvider } from "./providers/TranslationProvider";
import WidgetProvider from "./providers/WidgetProvider";
import "normalize.css";
import "./ChatWidget.css";
function ChatWidget(props: Config) {
return (

View File

@@ -6,19 +6,19 @@
* 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 { PropsWithChildren } from 'react';
import { PropsWithChildren } from "react";
import Launcher from './components/Launcher';
import UserSubscription from './components/UserSubscription';
import ChatProvider from './providers/ChatProvider';
import { ColorProvider } from './providers/ColorProvider';
import { Config, ConfigProvider } from './providers/ConfigProvider';
import { SettingsProvider } from './providers/SettingsProvider';
import { SocketProvider } from './providers/SocketProvider';
import { TranslationProvider } from './providers/TranslationProvider';
import WidgetProvider, { WidgetContextType } from './providers/WidgetProvider';
import './UiChatWidget.css';
import { ConnectionState } from './types/state.types';
import Launcher from "./components/Launcher";
import UserSubscription from "./components/UserSubscription";
import ChatProvider from "./providers/ChatProvider";
import { ColorProvider } from "./providers/ColorProvider";
import { Config, ConfigProvider } from "./providers/ConfigProvider";
import { SettingsProvider } from "./providers/SettingsProvider";
import { SocketProvider } from "./providers/SocketProvider";
import { TranslationProvider } from "./providers/TranslationProvider";
import WidgetProvider, { WidgetContextType } from "./providers/WidgetProvider";
import "./UiChatWidget.css";
import { ConnectionState } from "./types/state.types";
type UiChatWidgetProps = PropsWithChildren<{
CustomLauncher?: (props: { widget: WidgetContextType }) => JSX.Element;

View File

@@ -1,7 +1,7 @@
.sc-header {
min-height: 64px;
border-top-left-radius: .5rem;
border-top-right-radius: .5rem;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
padding: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
position: relative;
@@ -54,7 +54,7 @@
height: 100%;
padding: 5px;
box-sizing: border-box;
fill: #FFF;
fill: #fff;
}
@media (max-width: 450px) {

View File

@@ -6,15 +6,15 @@
* 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 { FC, PropsWithChildren } from 'react';
import { FC, PropsWithChildren } from "react";
import { useColors } from '../providers/ColorProvider';
import { useSettings } from '../providers/SettingsProvider';
import { useWidget } from '../providers/WidgetProvider';
import { useColors } from "../providers/ColorProvider";
import { useSettings } from "../providers/SettingsProvider";
import { useWidget } from "../providers/WidgetProvider";
import './ChatHeader.scss';
import CloseIcon from './icons/CloseIcon';
import OpenIcon from './icons/OpenIcon';
import "./ChatHeader.scss";
import CloseIcon from "./icons/CloseIcon";
import OpenIcon from "./icons/OpenIcon";
type ChatHeaderProps = PropsWithChildren;

View File

@@ -12,7 +12,8 @@
flex-direction: column;
justify-content: space-between;
border-radius: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
animation: fadeIn;
animation-duration: 0.3s;
animation-timing-function: ease-in-out;

View File

@@ -6,17 +6,17 @@
* 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, { PropsWithChildren } from 'react';
import React, { PropsWithChildren } from "react";
import { useChat } from '../providers/ChatProvider';
import { useWidget } from '../providers/WidgetProvider';
import { useChat } from "../providers/ChatProvider";
import { useWidget } from "../providers/WidgetProvider";
import ChatHeader from './ChatHeader';
import ConnectionLost from './ConnectionLost';
import Messages from './Messages';
import UserInput from './UserInput';
import Webview from './Webview';
import './ChatWindow.scss';
import ChatHeader from "./ChatHeader";
import ConnectionLost from "./ConnectionLost";
import Messages from "./Messages";
import UserInput from "./UserInput";
import Webview from "./Webview";
import "./ChatWindow.scss";
type ChatWindowProps = PropsWithChildren<{
CustomHeader?: () => JSX.Element;
@@ -35,18 +35,18 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
const { screen, isOpen } = useWidget();
return (
<div className={`sc-chat-window ${isOpen ? 'opened' : 'closed'}`}>
<div className={`sc-chat-window ${isOpen ? "opened" : "closed"}`}>
<ChatHeader>{CustomHeader && <CustomHeader />}</ChatHeader>
{screen === 'prechat' && PreChat && <PreChat />}
{['prechat', 'postchat', 'webview'].indexOf(screen) === -1 &&
{screen === "prechat" && PreChat && <PreChat />}
{["prechat", "postchat", "webview"].indexOf(screen) === -1 &&
connectionState === 3 && <Messages Avatar={CustomAvatar} />}
{screen !== 'prechat' &&
screen !== 'postchat' &&
{screen !== "prechat" &&
screen !== "postchat" &&
connectionState !== 3 && <ConnectionLost />}
{screen === 'postchat' && PostChat && <PostChat />}
{['prechat', 'postchat', 'webview'].indexOf(screen) === -1 &&
{screen === "postchat" && PostChat && <PostChat />}
{["prechat", "postchat", "webview"].indexOf(screen) === -1 &&
connectionState === 3 && <UserInput />}
{screen === 'webview' && <Webview />}
{screen === "webview" && <Webview />}
</div>
);
};

View File

@@ -29,7 +29,7 @@
outline: 0;
}
.sc-chat--disconnected-button:active {
content: '';
content: "";
opacity: 0;
transition: all 0.5s;
}

View File

@@ -6,15 +6,15 @@
* 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 from 'react';
import React from "react";
import { useTranslation } from '../hooks/useTranslation';
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useTranslation } from "../hooks/useTranslation";
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import ConnectionIcon from './icons/ConnectionIcon';
import LoadingIcon from './icons/LoadingIcon';
import './ConnectionLost.scss';
import ConnectionIcon from "./icons/ConnectionIcon";
import LoadingIcon from "./icons/LoadingIcon";
import "./ConnectionLost.scss";
const ConnectionLost: React.FC = () => {
const { t } = useTranslation();
@@ -41,7 +41,7 @@ const ConnectionLost: React.FC = () => {
className="sc-chat--disconnected-text"
style={{ color: colors.button.text }}
>
{t('settings.connection_lost')}
{t("settings.connection_lost")}
</h3>
<button
className="sc-chat--disconnected-button"

View File

@@ -1,4 +1,3 @@
.sc-emoji-picker {
position: absolute;
bottom: 30px;
@@ -24,7 +23,7 @@
}
.sc-emoji-picker--content {
padding: .5rem;
padding: 0.5rem;
overflow: auto;
width: 100%;
max-height: 195px;
@@ -54,7 +53,9 @@
cursor: pointer;
vertical-align: middle;
font-size: 1.5rem;
transition: transform 60ms ease-out,-webkit-transform 60ms ease-out;
transition:
transform 60ms ease-out,
-webkit-transform 60ms ease-out;
}
.sc-emoji-picker--emoji:hover {

View File

@@ -6,12 +6,12 @@
* 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 EmojiConvertor from 'emoji-js';
import React, { useEffect, useRef } from 'react';
import EmojiConvertor from "emoji-js";
import React, { useEffect, useRef } from "react";
import emojiData from '../constants/emojiData';
import emojiData from "../constants/emojiData";
import './EmojiPicker.scss';
import "./EmojiPicker.scss";
interface EmojiPickerProps {
onBlur: (event: React.FocusEvent<HTMLDivElement>) => void;
@@ -26,10 +26,10 @@ const EmojiPicker: React.FC<EmojiPickerProps> = ({ onBlur, onSelect }) => {
const elem = domNode.current;
if (elem) {
elem.style.opacity = '0';
elem.style.opacity = "0";
window.requestAnimationFrame(() => {
elem.style.transition = 'opacity 350ms';
elem.style.opacity = '1';
elem.style.transition = "opacity 350ms";
elem.style.opacity = "1";
});
elem.focus();
// @ts-expect-error ts error

View File

@@ -6,17 +6,17 @@
* 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, { PropsWithChildren } from 'react';
import React, { PropsWithChildren } from "react";
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useSocketLifecycle } from '../providers/SocketProvider';
import { useWidget, WidgetContextType } from '../providers/WidgetProvider';
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import { useSocketLifecycle } from "../providers/SocketProvider";
import { useWidget, WidgetContextType } from "../providers/WidgetProvider";
import ChatWindow from './ChatWindow';
import CloseIcon from './icons/CloseIcon';
import OpenIcon from './icons/OpenIcon';
import './Launcher.scss';
import ChatWindow from "./ChatWindow";
import CloseIcon from "./icons/CloseIcon";
import OpenIcon from "./icons/OpenIcon";
import "./Launcher.scss";
type LauncherProps = PropsWithChildren<{
CustomLauncher?: (props: { widget: WidgetContextType }) => JSX.Element;
@@ -46,7 +46,7 @@ const Launcher: React.FC<LauncherProps> = ({
return (
<div>
<div
className={`sc-launcher-wrapper ${widget.isOpen ? 'opened' : ''}`}
className={`sc-launcher-wrapper ${widget.isOpen ? "opened" : ""}`}
onClick={handleToggle}
>
{CustomLauncher ? (

View File

@@ -6,13 +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).
*/
import React from 'react';
import React from "react";
import { useColors } from '../providers/ColorProvider';
import { IMenuNode } from '../types/menu.type';
import { IPayload } from '../types/message.types';
import { useColors } from "../providers/ColorProvider";
import { IMenuNode } from "../types/menu.type";
import { IPayload } from "../types/message.types";
import './MenuItem.scss';
import "./MenuItem.scss";
interface MenuItemProps {
item: IMenuNode;
@@ -30,13 +30,13 @@ const MenuItem: React.FC<MenuItemProps> = ({
const { colors } = useColors();
const handleClick = () => {
switch (item.type) {
case 'web_url':
window.open(item.url, '_blank');
case "web_url":
window.open(item.url, "_blank");
break;
case 'nested':
case "nested":
onOpenSubItems({ ...item, _parent: parent });
break;
case 'postback':
case "postback":
onPostback({ text: item.title, payload: item.payload });
break;
}
@@ -51,7 +51,7 @@ const MenuItem: React.FC<MenuItemProps> = ({
onClick={handleClick}
>
{item.title}
{item.type === 'nested' && (
{item.type === "nested" && (
<span className="sc-menu-item-button">&#10095;</span>
)}
</a>

View File

@@ -59,9 +59,9 @@
.sc-message--text {
padding: 10px 20px;
border-radius: .5rem;
border-radius: 0.5rem;
font-weight: 400;
font-size: .9rem;
font-size: 0.9rem;
line-height: 1.5;
position: relative;
-webkit-font-smoothing: subpixel-antialiased;

View File

@@ -6,25 +6,25 @@
* 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 dayjs from 'dayjs';
import 'dayjs/locale/en';
import 'dayjs/locale/fr';
import relativeTime from 'dayjs/plugin/relativeTime';
import React, { PropsWithChildren, useState } from 'react';
import dayjs from "dayjs";
import "dayjs/locale/en";
import "dayjs/locale/fr";
import relativeTime from "dayjs/plugin/relativeTime";
import React, { PropsWithChildren, useState } from "react";
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { TMessage } from '../types/message.types';
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import { TMessage } from "../types/message.types";
import ChatIcon from './icons/ChatIcon';
import './Message.scss';
import ButtonsMessage from './messages/ButtonMessage';
import CarouselMessage from './messages/CarouselMessage';
import FileMessage from './messages/FileMessage';
import GeolocationMessage from './messages/GeolocationMessage';
import ListMessage from './messages/ListMessage';
import TextMessage from './messages/TextMessage';
import MessageStatus from './MessageStatus';
import ChatIcon from "./icons/ChatIcon";
import "./Message.scss";
import ButtonsMessage from "./messages/ButtonMessage";
import CarouselMessage from "./messages/CarouselMessage";
import FileMessage from "./messages/FileMessage";
import GeolocationMessage from "./messages/GeolocationMessage";
import ListMessage from "./messages/ListMessage";
import TextMessage from "./messages/TextMessage";
import MessageStatus from "./MessageStatus";
dayjs.extend(relativeTime);
@@ -40,8 +40,8 @@ const Message: React.FC<MessageProps> = ({ message, Avatar }) => {
const user = participants.find(
(participant) => participant.id === message.author,
) || {
id: 'me',
name: 'Anon',
id: "me",
name: "Anon",
};
const handleTime = () => {
setIsTimeVisible(!isTimeVisible);
@@ -73,21 +73,21 @@ const Message: React.FC<MessageProps> = ({ message, Avatar }) => {
) : null}
</div>
<div className="sc-message--wrapper" onClick={handleTime}>
{message.data && 'text' in message.data && (
{message.data && "text" in message.data && (
<TextMessage message={message} />
)}
{message.type === 'file' && <FileMessage message={message} />}
{message.type === 'location' && (
{message.type === "file" && <FileMessage message={message} />}
{message.type === "location" && (
<GeolocationMessage message={message} />
)}
{message.type === 'list' && <ListMessage messageList={message} />}
{message.type === 'carousel' && (
{message.type === "list" && <ListMessage messageList={message} />}
{message.type === "carousel" && (
<CarouselMessage messageCarousel={message} />
)}
{message.type === 'buttons' && <ButtonsMessage message={message} />}
{message.type === "buttons" && <ButtonsMessage message={message} />}
<div className="sc-message--meta">
{message.direction === 'sent' && (
{message.direction === "sent" && (
<MessageStatus message={message} />
)}
<div

View File

@@ -6,13 +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).
*/
import React from 'react';
import React from "react";
import { useColors } from '../providers/ColorProvider';
import { TMessage } from '../types/message.types';
import { useColors } from "../providers/ColorProvider";
import { TMessage } from "../types/message.types";
import CheckIcon from './icons/CheckIcon';
import './MessageStatus.scss';
import CheckIcon from "./icons/CheckIcon";
import "./MessageStatus.scss";
interface MessageStatusProps {
message: TMessage;
@@ -21,8 +21,8 @@ interface MessageStatusProps {
const MessageStatus: React.FC<MessageStatusProps> = ({ message }) => {
const { colors } = useColors();
if (!('delivery' in message && 'read' in message)) {
throw new Error('Unable to find delivery/read attributes');
if (!("delivery" in message && "read" in message)) {
throw new Error("Unable to find delivery/read attributes");
}
return (

View File

@@ -35,7 +35,7 @@ const Messages: React.FC<MessagesProps> = ({ Avatar }) => {
if (scrollListRef.current) {
const scrollPercent = Math.round(
(100 * scrollListRef.current.scrollTop) /
(scrollListRef.current.scrollHeight || 1)
(scrollListRef.current.scrollHeight || 1),
);
if (Math.abs(scrollPercent - scroll) > 1 || scroll === 100) {
@@ -43,7 +43,7 @@ const Messages: React.FC<MessagesProps> = ({ Avatar }) => {
if (scrollListRef.current) {
scrollListRef.current.scrollTo({
top: Math.round(
(scroll * scrollListRef.current.scrollHeight) / 100
(scroll * scrollListRef.current.scrollHeight) / 100,
),
behavior: "instant",
left: 0,

View File

@@ -6,14 +6,14 @@
* 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 from 'react';
import React from "react";
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useSettings } from '../providers/SettingsProvider';
import { ISuggestion, TOutgoingMessageType } from '../types/message.types';
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import { useSettings } from "../providers/SettingsProvider";
import { ISuggestion, TOutgoingMessageType } from "../types/message.types";
import './Suggestions.scss';
import "./Suggestions.scss";
const Suggestions: React.FC = () => {
const { setPayload, send, suggestions } = useChat();
@@ -26,7 +26,7 @@ const Suggestions: React.FC = () => {
setPayload(suggestion);
send({
event,
source: 'quick-reply',
source: "quick-reply",
data: {
type: TOutgoingMessageType.quick_reply,
data: suggestion,

View File

@@ -5,9 +5,11 @@
bottom: 0;
display: flex;
background-color: #f4f7f9;
border-bottom-left-radius: .5rem;
border-bottom-right-radius: .5rem;
transition: background-color 0.2s ease, box-shadow 0.2s ease;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
transition:
background-color 0.2s ease,
box-shadow 0.2s ease;
border-top: 1px solid #eef2f4;
}
@@ -16,7 +18,7 @@
resize: none;
border: none;
outline: none;
border-bottom-left-radius: .5rem;
border-bottom-left-radius: 0.5rem;
box-sizing: border-box;
padding: 18px 8px;
font-size: 15px;
@@ -42,18 +44,18 @@
cursor: text;
}
.sc-user-input--text[contenteditable='true']:focus:empty:before {
.sc-user-input--text[contenteditable="true"]:focus:empty:before {
position: absolute;
}
.sc-user-input--text[contenteditable='true']:focus:empty:after {
content: '\00a0';
.sc-user-input--text[contenteditable="true"]:focus:empty:after {
content: "\00a0";
}
.sc-user-input--buttons {
display: flex;
gap: 4px;
margin: 0 .5rem;
margin: 0 0.5rem;
}
.sc-user-input--button {

View File

@@ -6,25 +6,25 @@
* 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, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from '../hooks/useTranslation';
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useSettings } from '../providers/SettingsProvider';
import { TOutgoingMessageType } from '../types/message.types';
import { OutgoingMessageState } from '../types/state.types';
import { useTranslation } from "../hooks/useTranslation";
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import { useSettings } from "../providers/SettingsProvider";
import { TOutgoingMessageType } from "../types/message.types";
import { OutgoingMessageState } from "../types/state.types";
import EmojiButton from './buttons/EmojiButton';
import FileButton from './buttons/FileButton';
import LocationButton from './buttons/LocationButton';
import MenuButton from './buttons/MenuButton';
import SendButton from './buttons/SendButton';
import CloseIcon from './icons/CloseIcon';
import FileInputIcon from './icons/FileInputIcon';
import Suggestions from './Suggestions';
import EmojiButton from "./buttons/EmojiButton";
import FileButton from "./buttons/FileButton";
import LocationButton from "./buttons/LocationButton";
import MenuButton from "./buttons/MenuButton";
import SendButton from "./buttons/SendButton";
import CloseIcon from "./icons/CloseIcon";
import FileInputIcon from "./icons/FileInputIcon";
import Suggestions from "./Suggestions";
import './UserInput.scss';
import "./UserInput.scss";
const UserInput: React.FC = () => {
const { t } = useTranslation();
@@ -63,8 +63,8 @@ const UserInput: React.FC = () => {
}, [message, focusOnOpen]);
useEffect(() => {
if (message === '') {
userInputRef.current!.innerHTML = '';
if (message === "") {
userInputRef.current!.innerHTML = "";
}
}, [message]);
@@ -80,12 +80,12 @@ const UserInput: React.FC = () => {
setMessage(
userInputRef.current?.innerText ||
userInputRef.current?.textContent ||
'',
"",
);
};
const sendMessage = (
event: React.MouseEvent | React.KeyboardEvent,
source: string = 'send-button',
source: string = "send-button",
) => {
if (message) {
send({
@@ -97,7 +97,7 @@ const UserInput: React.FC = () => {
},
});
if (autoFlush) {
setMessage('');
setMessage("");
}
}
if (file) {
@@ -105,9 +105,9 @@ const UserInput: React.FC = () => {
const typeCheck = allowedUploadTypes.includes(file.type) || false;
if (!typeCheck) {
setFileError(t('messages.file_message.unsupported_file_type'));
setFileError(t("messages.file_message.unsupported_file_type"));
} else if (file.size > (allowedUploadSize || 0)) {
setFileError(t('messages.file_message.unsupported_file_size'));
setFileError(t("messages.file_message.unsupported_file_size"));
} else {
send({
event,
@@ -127,8 +127,8 @@ const UserInput: React.FC = () => {
}
};
const handleKey = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Enter' && !event.shiftKey) {
sendMessage(event, 'enter-key');
if (event.key === "Enter" && !event.shiftKey) {
sendMessage(event, "enter-key");
event.preventDefault();
}
};
@@ -170,7 +170,7 @@ const UserInput: React.FC = () => {
)}
<form
className={`sc-user-input ${inputActive ? 'active' : ''}`}
className={`sc-user-input ${inputActive ? "active" : ""}`}
style={{ background: colors.userInput.bg }}
>
{menu.length > 0 && <MenuButton />}

View File

@@ -12,22 +12,22 @@ import React, {
useEffect,
useRef,
useState,
} from 'react';
} from "react";
import { useTranslation } from '../hooks/useTranslation';
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useConfig } from '../providers/ConfigProvider';
import { useSettings } from '../providers/SettingsProvider';
import { useSocket } from '../providers/SocketProvider';
import { useWidget } from '../providers/WidgetProvider';
import { useTranslation } from "../hooks/useTranslation";
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import { useConfig } from "../providers/ConfigProvider";
import { useSettings } from "../providers/SettingsProvider";
import { useSocket } from "../providers/SocketProvider";
import { useWidget } from "../providers/WidgetProvider";
import {
Direction,
ISubscriber,
TMessage,
TOutgoingMessageType,
} from '../types/message.types';
import './UserSubscription.scss';
} from "../types/message.types";
import "./UserSubscription.scss";
const UserSubscription: React.FC = () => {
const config = useConfig();
@@ -43,8 +43,8 @@ const UserSubscription: React.FC = () => {
participants,
setParticipants,
} = useChat();
const [firstName, setFirstName] = useState<string>('');
const [lastName, setLastName] = useState<string>('');
const [firstName, setFirstName] = useState<string>("");
const [lastName, setLastName] = useState<string>("");
const isInitialized = useRef(false);
const handleSubmit = useCallback(
async (event?: React.FormEvent<HTMLFormElement>) => {
@@ -59,7 +59,7 @@ const UserSubscription: React.FC = () => {
);
const { messages, profile } = body;
localStorage.setItem('profile', JSON.stringify(profile));
localStorage.setItem("profile", JSON.stringify(profile));
messages.forEach((message) => {
const direction =
message.author === profile.foreign_id ||
@@ -86,23 +86,23 @@ const UserSubscription: React.FC = () => {
if (messages.length === 0) {
send({
event: event as SyntheticEvent,
source: 'get_started_button',
source: "get_started_button",
data: {
type: TOutgoingMessageType.postback,
data: {
text: t('messages.get_started'),
payload: 'GET_STARTED',
text: t("messages.get_started"),
payload: "GET_STARTED",
},
author: profile.foreign_id,
},
});
}
setConnectionState(3);
setScreen('chat');
setScreen("chat");
} catch (e) {
// eslint-disable-next-line no-console
console.error('Unable to subscribe user', e);
setScreen('prechat');
console.error("Unable to subscribe user", e);
setScreen("prechat");
setConnectionState(0);
}
},
@@ -123,7 +123,7 @@ const UserSubscription: React.FC = () => {
// User already subscribed ? (example : refreshed the page)
if (!isInitialized.current) {
isInitialized.current = true;
const profile = localStorage.getItem('profile');
const profile = localStorage.getItem("profile");
if (profile) {
const parsedProfile = JSON.parse(profile);
@@ -146,14 +146,14 @@ const UserSubscription: React.FC = () => {
className="user-subscription-form-input"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
placeholder={t('user_subscription.first_name')}
placeholder={t("user_subscription.first_name")}
required
/>
<input
className="user-subscription-form-input"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
placeholder={t('user_subscription.last_name')}
placeholder={t("user_subscription.last_name")}
required
/>
<button
@@ -161,7 +161,7 @@ const UserSubscription: React.FC = () => {
style={{ background: colors.header.bg, color: colors.header.text }}
className="user-subscription-form-button-submit"
>
{t('user_subscription.get_started')}
{t("user_subscription.get_started")}
</button>
</div>
</form>

View File

@@ -6,14 +6,14 @@
* 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, { useEffect, useState } from 'react';
import React, { useEffect, useState } from "react";
import { useTranslation } from '../hooks/useTranslation';
import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider';
import { useTranslation } from "../hooks/useTranslation";
import { useChat } from "../providers/ChatProvider";
import { useColors } from "../providers/ColorProvider";
import BackIcon from './icons/BackIcon';
import './Webview.scss';
import BackIcon from "./icons/BackIcon";
import "./Webview.scss";
const Webview: React.FC = () => {
const { t } = useTranslation();
@@ -26,7 +26,7 @@ const Webview: React.FC = () => {
}, []);
const close = () => {
setWebviewUrl('');
setWebviewUrl("");
};
return (
@@ -40,7 +40,7 @@ const Webview: React.FC = () => {
>
<h3 className="sc-webview--button" onClick={close}>
<BackIcon width="16px" height="16px" />
{t('settings.back')}
{t("settings.back")}
</h3>
</div>
</div>

View File

@@ -1,26 +1,25 @@
.sc-user-input--emoji-icon-wrapper {
background: none;
border: none;
padding: 0px;
margin: 0px;
outline: none;
opacity: .5;
}
.sc-user-input--emoji-icon-wrapper:focus {
outline: none;
}
.sc-user-input--emoji-icon {
width: 20px;
height: 20px;
cursor: pointer;
align-self: center;
vertical-align: middle;
}
.sc-user-input--emoji-icon-wrapper:focus,
.sc-user-input--emoji-icon-wrapper:hover {
opacity: 1;
}
background: none;
border: none;
padding: 0px;
margin: 0px;
outline: none;
opacity: 0.5;
}
.sc-user-input--emoji-icon-wrapper:focus {
outline: none;
}
.sc-user-input--emoji-icon {
width: 20px;
height: 20px;
cursor: pointer;
align-self: center;
vertical-align: middle;
}
.sc-user-input--emoji-icon-wrapper:focus,
.sc-user-input--emoji-icon-wrapper:hover {
opacity: 1;
}

View File

@@ -6,11 +6,11 @@
* 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, { RefObject, useRef, useState } from 'react';
import React, { RefObject, useRef, useState } from "react";
import EmojiPicker from '../EmojiPicker';
import './EmojiButton.scss';
import EmojiIcon from '../icons/EmojiIcon';
import EmojiPicker from "../EmojiPicker";
import "./EmojiButton.scss";
import EmojiIcon from "../icons/EmojiIcon";
const EmojiButton: React.FC<{
inputRef: RefObject<HTMLDivElement>;

View File

@@ -6,7 +6,7 @@
outline: none;
position: relative;
cursor: pointer;
opacity: .5;
opacity: 0.5;
}
.sc-user-input--file-icon-wrapper {
background: none;
@@ -26,5 +26,5 @@
}
.sc-user-input--file-wrapper:hover {
opacity: 1
opacity: 1;
}

View File

@@ -6,17 +6,17 @@
* 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, { ChangeEvent } from 'react';
import React, { ChangeEvent } from "react";
import { useChat } from '../../providers/ChatProvider';
import FileInputIcon from '../icons/FileInputIcon';
import { useChat } from "../../providers/ChatProvider";
import FileInputIcon from "../icons/FileInputIcon";
import './FileButton.scss';
import "./FileButton.scss";
const FileButton: React.FC = () => {
const { setFile } = useChat();
const handleClick = (e: React.MouseEvent<HTMLInputElement>) => {
(e.target as HTMLInputElement).value = '';
(e.target as HTMLInputElement).value = "";
};
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {

View File

@@ -4,7 +4,7 @@
padding: 0px;
margin: 0px;
outline: none;
opacity: .5;
opacity: 0.5;
}
.sc-user-input--location-icon-wrapper:focus {

View File

@@ -6,14 +6,14 @@
* 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 from 'react';
import React from "react";
import { useChat } from '../../providers/ChatProvider';
import { useSettings } from '../../providers/SettingsProvider';
import { TOutgoingMessageType } from '../../types/message.types';
import LocationIcon from '../icons/LocationIcon';
import { useChat } from "../../providers/ChatProvider";
import { useSettings } from "../../providers/SettingsProvider";
import { TOutgoingMessageType } from "../../types/message.types";
import LocationIcon from "../icons/LocationIcon";
import './LocationButton.scss';
import "./LocationButton.scss";
const LocationButton: React.FC = () => {
const { setPayload, send } = useChat();
@@ -30,7 +30,7 @@ const LocationButton: React.FC = () => {
});
send({
event,
source: 'geo-location',
source: "geo-location",
data: {
type: TOutgoingMessageType.location,
data: {
@@ -47,12 +47,12 @@ const LocationButton: React.FC = () => {
},
(error) => {
// eslint-disable-next-line no-console
console.error('Error getting location', error);
console.error("Error getting location", error);
},
);
} else {
// eslint-disable-next-line no-console
console.error('Geolocation is not supported by this browser.');
console.error("Geolocation is not supported by this browser.");
}
};

View File

@@ -6,17 +6,17 @@
* 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, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from "react";
import { useChat } from '../../providers/ChatProvider';
import { useColors } from '../../providers/ColorProvider';
import { useSettings } from '../../providers/SettingsProvider';
import { IMenuNode, MenuType } from '../../types/menu.type';
import { IPayload, TOutgoingMessageType } from '../../types/message.types';
import MenuIcon from '../icons/MenuIcon';
import MenuItem from '../MenuItem';
import { useChat } from "../../providers/ChatProvider";
import { useColors } from "../../providers/ColorProvider";
import { useSettings } from "../../providers/SettingsProvider";
import { IMenuNode, MenuType } from "../../types/menu.type";
import { IPayload, TOutgoingMessageType } from "../../types/message.types";
import MenuIcon from "../icons/MenuIcon";
import MenuItem from "../MenuItem";
import './MenuButton.scss';
import "./MenuButton.scss";
const MenuButton: React.FC = () => {
const { colors } = useColors();
@@ -24,7 +24,7 @@ const MenuButton: React.FC = () => {
const { send, setPayload } = useChat();
const [displayMenu, setDisplayMenu] = useState(false);
const [current, setCurrent] = useState<IMenuNode>({
title: 'Menu',
title: "Menu",
type: MenuType.nested,
call_to_actions: settings?.menu || [],
});
@@ -32,7 +32,7 @@ const MenuButton: React.FC = () => {
useEffect(() => {
setCurrent({
title: 'Menu',
title: "Menu",
type: MenuType.nested,
call_to_actions: settings?.menu || [],
});
@@ -49,7 +49,7 @@ const MenuButton: React.FC = () => {
const blur = (e: React.FocusEvent<HTMLDivElement>) => {
if (
!e.relatedTarget ||
(e.relatedTarget as HTMLElement).id !== 'sc-menu-button'
(e.relatedTarget as HTMLElement).id !== "sc-menu-button"
) {
setDisplayMenu(false);
}
@@ -61,8 +61,8 @@ const MenuButton: React.FC = () => {
setPayload(item);
send({
// @ts-expect-error todo
event: new Event('postback'),
source: 'persistent-menu',
event: new Event("postback"),
source: "persistent-menu",
data: {
type: TOutgoingMessageType.postback,
data: {

View File

@@ -6,10 +6,10 @@
* 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 from 'react';
import React from "react";
import SendIcon from '../icons/SendIcon';
import './SendButton.scss';
import SendIcon from "../icons/SendIcon";
import "./SendButton.scss";
interface SendButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}

View File

@@ -6,17 +6,17 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const BackIcon: FC<SVGProps<SVGSVGElement>> = ({
width = '24',
height = '24',
fill = 'none',
stroke = '#000',
strokeLinecap = 'round',
strokeLinejoin = 'round',
strokeWidth = '2',
viewBox = '0 0 24 24',
width = "24",
height = "24",
fill = "none",
stroke = "#000",
strokeLinecap = "round",
strokeLinejoin = "round",
strokeWidth = "2",
viewBox = "0 0 24 24",
...rest
}) => {
return (

View File

@@ -6,11 +6,11 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const ChatIcon: FC<SVGProps<SVGSVGElement>> = ({
width = '18',
height = '18',
width = "18",
height = "18",
...rest
}) => {
return (
@@ -32,7 +32,7 @@ const ChatIcon: FC<SVGProps<SVGSVGElement>> = ({
strokeWidth={4.4649702399999995}
paintOrder="normal"
style={{
mixBlendMode: 'normal',
mixBlendMode: "normal",
}}
transform="translate(-58.835 -133.808) translate(53.705 -18.313) scale(1.10427)"
/>

View File

@@ -6,15 +6,15 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const CheckIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox = '0 0 24 24',
fill = 'none',
stroke = '',
strokeWidth = '2',
strokeLinecap = 'round',
strokeLinejoin = 'round',
viewBox = "0 0 24 24",
fill = "none",
stroke = "",
strokeWidth = "2",
strokeLinecap = "round",
strokeLinejoin = "round",
...rest
}) => {
return (

View File

@@ -6,10 +6,10 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const CloseIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox = '0 0 24 24',
viewBox = "0 0 24 24",
...rest
}) => {
return (

View File

@@ -6,14 +6,14 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const ConnectionIcon: FC<SVGProps<SVGSVGElement>> = ({
width = '100',
height = '100',
x = '0',
y = '0',
viewBox = '0 0 512.115 512.115',
width = "100",
height = "100",
x = "0",
y = "0",
viewBox = "0 0 512.115 512.115",
...rest
}) => {
return (

View File

@@ -6,13 +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).
*/
import { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const EmojiIcon: FC<SVGProps<SVGSVGElement>> = ({
x = '0',
y = '0',
className = 'sc-user-input--emoji-icon',
viewBox = '0 0 24 24',
x = "0",
y = "0",
className = "sc-user-input--emoji-icon",
viewBox = "0 0 24 24",
...rest
}) => {
return (

View File

@@ -6,10 +6,10 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const FileIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox = '0 0 512 512',
viewBox = "0 0 512 512",
...rest
}) => {
return (

View File

@@ -6,13 +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).
*/
import { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const FileInputIcon: FC<SVGProps<SVGSVGElement>> = ({
x = '0',
y = '0',
className = 'sc-user-input--file-icon',
viewBox = '0 0 24 24',
x = "0",
y = "0",
className = "sc-user-input--file-icon",
viewBox = "0 0 24 24",
...rest
}) => {
return (
@@ -24,7 +24,7 @@ const FileInputIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox={viewBox}
{...rest}
>
<path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6z" />{' '}
<path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6z" />{" "}
</svg>
);
};

View File

@@ -6,14 +6,14 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const LoadingIcon: FC<
SVGProps<SVGSVGElement> & {
size?: number;
color?: string;
}
> = ({ size = 50, color = '#000', ...rest }) => {
> = ({ size = 50, color = "#000", ...rest }) => {
return (
<svg
width={size}

View File

@@ -6,14 +6,14 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const LocationIcon: FC<SVGProps<SVGSVGElement>> = ({
x = '0',
y = '0',
className = 'sc-user-input--location-icon',
version = '1.1',
viewBox = '0 0 24 24',
x = "0",
y = "0",
className = "sc-user-input--location-icon",
version = "1.1",
viewBox = "0 0 24 24",
...rest
}) => {
return (

View File

@@ -6,15 +6,15 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const MenuIcon: FC<SVGProps<SVGSVGElement>> = ({
width = '32',
height = '32',
x = '0',
y = '0',
className = 'sc-user-input--menu-img',
viewBox = '0 0 32 32',
width = "32",
height = "32",
x = "0",
y = "0",
className = "sc-user-input--menu-img",
viewBox = "0 0 32 32",
...rest
}) => {
return (

View File

@@ -6,11 +6,11 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const OpenIcon: FC<SVGProps<SVGSVGElement>> = ({
width = '18',
height = '18',
width = "18",
height = "18",
...rest
}) => {
return (
@@ -32,7 +32,7 @@ const OpenIcon: FC<SVGProps<SVGSVGElement>> = ({
strokeWidth={4.4649702399999995}
paintOrder="normal"
style={{
mixBlendMode: 'normal',
mixBlendMode: "normal",
}}
transform="translate(-58.835 -133.808) translate(53.705 -18.313) scale(1.10427)"
/>

View File

@@ -6,10 +6,10 @@
* 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 { FC, SVGProps } from 'react';
import { FC, SVGProps } from "react";
const SendIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox = '0 0 48 48',
viewBox = "0 0 48 48",
...rest
}) => {
return (

View File

@@ -2,7 +2,7 @@
color: rgb(34, 34, 34);
max-width: -webkit-fill-available;
padding: 0.25rem 0.5rem;
border-radius: .5rem;
border-radius: 0.5rem;
font-weight: 300;
font-size: 1.25rem;
line-height: 1.4;
@@ -18,7 +18,7 @@
margin: 2px;
cursor: pointer;
outline: 0;
font-size: .75rem;
font-size: 0.75rem;
}
}

View File

@@ -6,18 +6,18 @@
* 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 from 'react';
import React from "react";
import { useChat } from '../../providers/ChatProvider';
import { useColors } from '../../providers/ColorProvider';
import { useSettings } from '../../providers/SettingsProvider';
import { useChat } from "../../providers/ChatProvider";
import { useColors } from "../../providers/ColorProvider";
import { useSettings } from "../../providers/SettingsProvider";
import {
TButton,
TMessage,
TOutgoingMessageType,
} from '../../types/message.types';
} from "../../types/message.types";
import './ButtonMessage.scss';
import "./ButtonMessage.scss";
interface ButtonsMessageProps {
message: TMessage;
@@ -31,17 +31,17 @@ const ButtonsMessage: React.FC<ButtonsMessageProps> = ({ message }) => {
event: React.MouseEvent<HTMLButtonElement>,
button: TButton,
) => {
if (button.type === 'web_url' && button.url) {
if (button.type === "web_url" && button.url) {
if (button.messenger_extensions) {
setWebviewUrl(button.url);
} else {
window.open(button.url, '_blank');
window.open(button.url, "_blank");
}
} else if (button.type === 'postback') {
} else if (button.type === "postback") {
setPayload({ text: button.title, payload: button.payload });
send({
event,
source: 'post-back',
source: "post-back",
data: {
type: TOutgoingMessageType.postback,
data: {
@@ -56,8 +56,8 @@ const ButtonsMessage: React.FC<ButtonsMessageProps> = ({ message }) => {
}
};
if (!('buttons' in message.data)) {
throw new Error('Unable to find buttons');
if (!("buttons" in message.data)) {
throw new Error("Unable to find buttons");
}
return (

View File

@@ -1,5 +1,5 @@
.sc-message--carousel {
border-radius: .5rem;
border-radius: 0.5rem;
position: relative;
width: 100%;
overflow: hidden;
@@ -35,7 +35,7 @@
padding: 0.5rem 0;
margin-top: 8px;
}
.sc-message--carousel-element-description {
width: 100%;
}

View File

@@ -74,7 +74,7 @@ const CarouselMessage: React.FC<CarouselMessageProps> = ({
const items = messageCarousel.data.elements;
const goToPrevious = () => {
setActiveIndex(
(prevIndex) => (prevIndex + items.length - 1) % items.length
(prevIndex) => (prevIndex + items.length - 1) % items.length,
);
};
const goToNext = () => {

View File

@@ -1,6 +1,6 @@
.sc-message--file {
background-color: transparent !important;
border-radius: .5rem;
border-radius: 0.5rem;
font-weight: 300;
font-size: 14px;
line-height: 1.4;
@@ -31,7 +31,7 @@
.sc-message--file-download {
padding: 10px 20px;
border-radius: .5rem;
border-radius: 0.5rem;
color: white;
text-align: center;

View File

@@ -6,14 +6,14 @@
* 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 from 'react';
import React from "react";
import { useTranslation } from '../../hooks/useTranslation';
import { useColors } from '../../providers/ColorProvider';
import { TMessage } from '../../types/message.types';
import FileIcon from '../icons/FileIcon';
import { useTranslation } from "../../hooks/useTranslation";
import { useColors } from "../../providers/ColorProvider";
import { TMessage } from "../../types/message.types";
import FileIcon from "../icons/FileIcon";
import './FileMessage.scss';
import "./FileMessage.scss";
interface FileMessageProps {
message: TMessage;
@@ -22,20 +22,20 @@ interface FileMessageProps {
const FileMessage: React.FC<FileMessageProps> = ({ message }) => {
const { t } = useTranslation();
const { colors: allColors } = useColors();
const colors = allColors[message.direction || 'received'];
const colors = allColors[message.direction || "received"];
if (!('type' in message.data)) {
throw new Error('Unable to detect type for file message');
if (!("type" in message.data)) {
throw new Error("Unable to detect type for file message");
}
if (
message.data &&
message.data.type !== 'image' &&
message.data.type !== 'audio' &&
message.data.type !== 'video' &&
message.data.type !== 'file'
message.data.type !== "image" &&
message.data.type !== "audio" &&
message.data.type !== "video" &&
message.data.type !== "file"
) {
throw new Error('Uknown type for file message');
throw new Error("Uknown type for file message");
}
return (
@@ -46,28 +46,28 @@ const FileMessage: React.FC<FileMessageProps> = ({ message }) => {
backgroundColor: colors.bg,
}}
>
{message.data.type === 'image' && (
{message.data.type === "image" && (
<div className="sc-message--file-icon">
<img src={message.data.url || ''} className="sc-image" alt="File" />
<img src={message.data.url || ""} className="sc-image" alt="File" />
</div>
)}
{message.data.type === 'audio' && (
{message.data.type === "audio" && (
<div className="sc-message--file-audio">
<audio controls>
<source src={message.data.url} />
{t('messages.file_message.browser_audio_unsupport')}
{t("messages.file_message.browser_audio_unsupport")}
</audio>
</div>
)}
{message.data.type === 'video' && (
{message.data.type === "video" && (
<div className="sc-message--file-video">
<video controls width="100%">
<source src={message.data.url} />
{t('messages.file_message.browser_video_unsupport')}
{t("messages.file_message.browser_video_unsupport")}
</video>
</div>
)}
{message.data.type === 'file' && (
{message.data.type === "file" && (
<div
className="sc-message--file-download"
style={{
@@ -76,13 +76,13 @@ const FileMessage: React.FC<FileMessageProps> = ({ message }) => {
}}
>
<a
href={message.data.url ? message.data.url : '#'}
href={message.data.url ? message.data.url : "#"}
target="_blank"
rel="noopener noreferrer"
download
>
<FileIcon />
{t('messages.file_message.download')}
{t("messages.file_message.download")}
</a>
</div>
)}

View File

@@ -1,8 +1,8 @@
.sc-message--location {
border-radius: .5rem;
border-radius: 0.5rem;
}
.sc-message-map {
width: 200px;
height: 150px;
border-radius: .5rem;
border-radius: 0.5rem;
}

View File

@@ -6,13 +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).
*/
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from "react";
import { useColors } from '../../providers/ColorProvider';
import { useWidget } from '../../providers/WidgetProvider';
import { TMessage } from '../../types/message.types';
import { useColors } from "../../providers/ColorProvider";
import { useWidget } from "../../providers/WidgetProvider";
import { TMessage } from "../../types/message.types";
import './GeolocationMessage.scss';
import "./GeolocationMessage.scss";
interface GeolocationMessageProps {
message: TMessage;
@@ -49,8 +49,8 @@ const GeolocationMessage: React.FC<GeolocationMessageProps> = ({ message }) => {
}
}, [isSeen, widget]);
if (!('coordinates' in message.data)) {
throw new Error('Unable to find coordinates');
if (!("coordinates" in message.data)) {
throw new Error("Unable to find coordinates");
}
const coordinates = message.data?.coordinates || { lat: 0.0, lng: 0.0 };
const openStreetMapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=${
@@ -58,7 +58,7 @@ const GeolocationMessage: React.FC<GeolocationMessageProps> = ({ message }) => {
},${coordinates.lat - 0.1},${coordinates.lng + 0.1},${
coordinates.lat + 0.1
}&layer=mapnik&marker=${coordinates.lat},${coordinates.lng}`;
const colors = allColors[message.direction || 'received'];
const colors = allColors[message.direction || "received"];
return (
<div

View File

@@ -1,5 +1,5 @@
.sc-message--list {
border-radius: .5rem;
border-radius: 0.5rem;
width: 256px;
.sc-message--list-element {
@@ -19,12 +19,12 @@
.sc-message--list-element-image {
background-size: cover;
height: auto;
border-radius: .5rem .5rem 0 0;
border-radius: 0.5rem 0.5rem 0 0;
}
.sc-message--list-element-description {
color: #fff;
border-radius: .5rem .5rem 0 0;
border-radius: 0.5rem 0.5rem 0 0;
background: rgba(0, 0, 0, 0.5);
}
}
@@ -39,7 +39,7 @@
margin: 0;
white-space: pre-line;
}
.sc-message--list-element-image {
background-position: center;
background-repeat: no-repeat;

View File

@@ -6,14 +6,14 @@
* 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 from 'react';
import React from "react";
import { useColors } from '../../providers/ColorProvider';
import { TMessage } from '../../types/message.types';
import { useColors } from "../../providers/ColorProvider";
import { TMessage } from "../../types/message.types";
import ButtonsMessage from './ButtonMessage';
import ButtonsMessage from "./ButtonMessage";
import './ListMessage.scss';
import "./ListMessage.scss";
interface ListMessageProps {
messageList: TMessage;
@@ -29,17 +29,17 @@ const ListMessage: React.FC<ListMessageProps> = ({ messageList }) => {
return result;
};
const truncate = (string: string, length: number = 100) => {
return string.length > length ? string.substr(0, length) + '...' : string;
return string.length > length ? string.substr(0, length) + "..." : string;
};
const linebreak = (string: string) => {
return string.replace(/\n/g, '<br />');
return string.replace(/\n/g, "<br />");
};
if (!('elements' in messageList.data)) {
throw new Error('Unable to find elements');
if (!("elements" in messageList.data)) {
throw new Error("Unable to find elements");
}
const colors = allColors[messageList.direction || 'received'];
const colors = allColors[messageList.direction || "received"];
return (
<div
@@ -52,10 +52,10 @@ const ListMessage: React.FC<ListMessageProps> = ({ messageList }) => {
{messageList.data.elements.map((message, idx) => {
const mode =
idx === 0 &&
'top_element_style' in messageList.data &&
messageList.data.top_element_style === 'large'
? 'large'
: 'compact';
"top_element_style" in messageList.data &&
messageList.data.top_element_style === "large"
? "large"
: "compact";
return (
<div
@@ -69,7 +69,7 @@ const ListMessage: React.FC<ListMessageProps> = ({ messageList }) => {
className="sc-message--list-element-image"
style={{ backgroundImage: `url('${message.image_url}')` }}
>
{mode === 'large' && (
{mode === "large" && (
<div className="sc-message--list-element-description">
<h3 className="sc-message--title">{message.title}</h3>
{message.subtitle && (
@@ -83,7 +83,7 @@ const ListMessage: React.FC<ListMessageProps> = ({ messageList }) => {
)}
</div>
)}
{mode === 'compact' && (
{mode === "compact" && (
<div className="sc-message--list-element-description">
<h3 className="sc-message--title">{message.title}</h3>
{message.subtitle && (
@@ -104,7 +104,7 @@ const ListMessage: React.FC<ListMessageProps> = ({ messageList }) => {
</div>
);
})}
{'buttons' in messageList.data &&
{"buttons" in messageList.data &&
Array.isArray(messageList.data.buttons) &&
messageList.data.buttons.length > 0 && (
<div className="sc-message--list-element-bottom">

View File

@@ -6,13 +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).
*/
import Autolinker from 'autolinker';
import React, { useEffect, useRef } from 'react';
import Autolinker from "autolinker";
import React, { useEffect, useRef } from "react";
import { useColors } from '../../providers/ColorProvider';
import { TMessage } from '../../types/message.types';
import { useColors } from "../../providers/ColorProvider";
import { TMessage } from "../../types/message.types";
import './TextMessage.scss';
import "./TextMessage.scss";
interface TextMessageProps {
message: TMessage;
@@ -28,21 +28,21 @@ const TextMessage: React.FC<TextMessageProps> = ({ message }) => {
}, [message]);
const autoLink = () => {
if (message.direction === 'received' && messageTextRef.current) {
if (message.direction === "received" && messageTextRef.current) {
const text = messageTextRef.current.innerText;
messageTextRef.current.innerHTML = Autolinker.link(text, {
className: 'chatLink',
truncate: { length: 50, location: 'smart' },
className: "chatLink",
truncate: { length: 50, location: "smart" },
});
}
};
if (!('text' in message.data)) {
throw new Error('Unable to find text.');
if (!("text" in message.data)) {
throw new Error("Unable to find text.");
}
const colors = allColors[message.direction || 'received'];
const colors = allColors[message.direction || "received"];
return (
<div

View File

@@ -1,6 +1,6 @@
.sc-typing-indicator {
text-align: center;
padding: .5rem;
padding: 0.5rem;
border-radius: 2rem;
width: 50px;
margin-left: 2rem;

View File

@@ -6,11 +6,11 @@
* 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 from 'react';
import React from "react";
import { useColors } from '../../providers/ColorProvider';
import { useColors } from "../../providers/ColorProvider";
import './TypingMessage.scss';
import "./TypingMessage.scss";
const TypingMessage: React.FC = () => {
const { colors } = useColors();

View File

@@ -6,217 +6,217 @@
* 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 { ColorState } from '../types/colors.types';
import { ColorState } from "../types/colors.types";
const colors: Record<string, ColorState> = {
orange: {
header: {
bg: '#FF851B',
text: '#fff',
bg: "#FF851B",
text: "#fff",
},
launcher: {
bg: '#FF851B',
bg: "#FF851B",
},
messageList: {
bg: '#fff',
bg: "#fff",
},
sent: {
bg: '#FF851B',
text: '#fff',
bg: "#FF851B",
text: "#fff",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#ffffff',
text: '#FF851B',
border: '#FF851B',
bg: "#ffffff",
text: "#FF851B",
border: "#FF851B",
},
messageStatus: {
bg: '#FF851B',
bg: "#FF851B",
},
messageTime: {
text: '#9C9C9C',
text: "#9C9C9C",
},
},
red: {
header: {
bg: '#FF4136',
text: '#fff',
bg: "#FF4136",
text: "#fff",
},
launcher: {
bg: '#FF4136',
bg: "#FF4136",
},
messageList: {
bg: '#fff',
bg: "#fff",
},
sent: {
bg: '#FF4136',
text: '#fff',
bg: "#FF4136",
text: "#fff",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#ffffff',
text: '#FF4136',
border: '#FF4136',
bg: "#ffffff",
text: "#FF4136",
border: "#FF4136",
},
messageStatus: {
bg: '#FF4136',
bg: "#FF4136",
},
messageTime: {
text: '#FF4136',
text: "#FF4136",
},
},
green: {
header: {
bg: '#2ECC40',
text: '#fff',
bg: "#2ECC40",
text: "#fff",
},
launcher: {
bg: '#2ECC40',
bg: "#2ECC40",
},
messageList: {
bg: '#fff',
bg: "#fff",
},
sent: {
bg: '#2ECC40',
text: '#fff',
bg: "#2ECC40",
text: "#fff",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#ffffff',
text: '#2ECC40',
border: '#2ECC40',
bg: "#ffffff",
text: "#2ECC40",
border: "#2ECC40",
},
messageStatus: {
bg: '#2ECC40',
bg: "#2ECC40",
},
messageTime: {
text: '#2ECC40',
text: "#2ECC40",
},
},
blue: {
header: {
bg: '#0074D9',
text: '#fff',
bg: "#0074D9",
text: "#fff",
},
launcher: {
bg: '#0074D9',
bg: "#0074D9",
},
messageList: {
bg: '#fff',
bg: "#fff",
},
sent: {
bg: '#0074D9',
text: '#fff',
bg: "#0074D9",
text: "#fff",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#ffffff',
text: '#0074D9',
border: '#0074D9',
bg: "#ffffff",
text: "#0074D9",
border: "#0074D9",
},
messageStatus: {
bg: '#0074D9',
bg: "#0074D9",
},
messageTime: {
text: '#0074D9',
text: "#0074D9",
},
},
teal: {
header: {
bg: '#1BA089',
text: '#fff',
bg: "#1BA089",
text: "#fff",
},
launcher: {
bg: '#1BA089',
bg: "#1BA089",
},
messageList: {
bg: '#fff',
bg: "#fff",
},
sent: {
bg: '#1BA089',
text: '#fff',
bg: "#1BA089",
text: "#fff",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#ffffff',
text: '#1BA089',
border: '#1BA089',
bg: "#ffffff",
text: "#1BA089",
border: "#1BA089",
},
messageStatus: {
bg: '#1BA089',
bg: "#1BA089",
},
messageTime: {
text: '#9C9C9C',
text: "#9C9C9C",
},
},
dark: {
header: {
bg: '#000',
text: '#ecf0f1',
bg: "#000",
text: "#ecf0f1",
},
launcher: {
bg: '#000',
bg: "#000",
},
messageList: {
bg: '#FFF',
bg: "#FFF",
},
sent: {
bg: '#000',
text: '#FFF',
bg: "#000",
text: "#FFF",
},
received: {
bg: '#f6f8f9',
text: '#000',
bg: "#f6f8f9",
text: "#000",
},
userInput: {
bg: '#fff',
text: '#000',
bg: "#fff",
text: "#000",
},
button: {
bg: '#000',
text: '#ecf0f1',
border: '#34495e',
bg: "#000",
text: "#ecf0f1",
border: "#34495e",
},
messageStatus: {
bg: '#95a5a6',
bg: "#95a5a6",
},
messageTime: {
text: '#ffffff',
text: "#ffffff",
},
},
};

View File

@@ -7,7 +7,7 @@
*/
export const DEFAULT_CONFIG = {
apiUrl: process.env.REACT_APP_WIDGET_API_URL || 'http://localhost:4000',
channel: process.env.REACT_APP_WIDGET_CHANNEL || 'console-channel',
language: 'en',
apiUrl: process.env.REACT_APP_WIDGET_API_URL || "http://localhost:4000",
channel: process.env.REACT_APP_WIDGET_CHANNEL || "console-channel",
language: "en",
};

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
* 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 { useEffect, useState } from 'react';
import { useEffect, useState } from "react";
type UseSocketGetQueryReturnType<T> = {
data: T | null;

View File

@@ -6,9 +6,9 @@
* 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 { useEffect, useState } from 'react';
import { useEffect, useState } from "react";
import { useSocket } from '../providers/SocketProvider';
import { useSocket } from "../providers/SocketProvider";
type UseSocketGetQueryReturnType<T> = {
data: T | null;

View File

@@ -6,9 +6,9 @@
* 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 { useCallback } from 'react';
import { useCallback } from "react";
import { useTranslations } from '../providers/TranslationProvider';
import { useTranslations } from "../providers/TranslationProvider";
// Define a recursive interface for nested objects
interface NestedTranslation {
@@ -20,9 +20,9 @@ const getNestedTranslation = (
path: string,
): string | undefined => {
return path
.split('.')
.split(".")
.reduce((acc: NestedTranslation | string | undefined, part) => {
if (typeof acc === 'object' && acc !== null) {
if (typeof acc === "object" && acc !== null) {
return acc[part];
}

View File

@@ -1,3 +1,2 @@
:root {
}

View File

@@ -6,20 +6,20 @@
* 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 from 'react';
import ReactDOM from 'react-dom/client';
import React from "react";
import ReactDOM from "react-dom/client";
import ChatWidget from './ChatWidget.tsx';
import ChatWidget from "./ChatWidget.tsx";
import './index.css';
import "./index.css";
ReactDOM.createRoot(document.getElementById('root')!).render(
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ChatWidget
{...{
apiUrl: process.env.REACT_APP_WIDGET_API_URL || 'http://localhost:4000',
channel: process.env.REACT_APP_WIDGET_CHANNEL || 'web-channel',
language: 'en',
apiUrl: process.env.REACT_APP_WIDGET_API_URL || "http://localhost:4000",
channel: process.env.REACT_APP_WIDGET_CHANNEL || "web-channel",
language: "en",
}}
/>
</React.StrictMode>,

View File

@@ -14,9 +14,9 @@ import React, {
useContext,
useEffect,
useState,
} from 'react';
} from "react";
import { StdEventType } from '../types/chat-io-messages.types';
import { StdEventType } from "../types/chat-io-messages.types";
import {
Direction,
IPayload,
@@ -26,13 +26,13 @@ import {
TEvent,
TMessage,
TPostMessageEvent,
} from '../types/message.types';
import { ConnectionState, OutgoingMessageState } from '../types/state.types';
} from "../types/message.types";
import { ConnectionState, OutgoingMessageState } from "../types/state.types";
import { useConfig } from './ConfigProvider';
import { useSettings } from './SettingsProvider';
import { useSocket, useSubscribe } from './SocketProvider';
import { useWidget } from './WidgetProvider';
import { useConfig } from "./ConfigProvider";
import { useSettings } from "./SettingsProvider";
import { useSocket, useSubscribe } from "./SocketProvider";
import { useWidget } from "./WidgetProvider";
interface Participant {
id: string;
@@ -149,10 +149,10 @@ interface ChatContextType {
const defaultCtx: ChatContextType = {
participants: [
{
id: 'chatbot',
name: 'Hexabot',
foreign_id: 'chatbot',
imageUrl: '',
id: "chatbot",
name: "Hexabot",
foreign_id: "chatbot",
imageUrl: "",
},
],
setParticipants: () => {},
@@ -170,9 +170,9 @@ const defaultCtx: ChatContextType = {
setNewIOMessage: () => {},
newMessagesCount: 0,
setNewMessagesCount: () => {},
webviewUrl: '',
webviewUrl: "",
setWebviewUrl: () => {},
message: '',
message: "",
setMessage: () => {},
payload: null,
setPayload: () => {},
@@ -228,8 +228,8 @@ const ChatProvider: React.FC<{
setNewIOMessage(newIOMessage);
if (
newIOMessage &&
'type' in newIOMessage &&
newIOMessage.type === 'typing'
"type" in newIOMessage &&
newIOMessage.type === "typing"
) {
return showTypingIndicator === true;
}
@@ -237,12 +237,12 @@ const ChatProvider: React.FC<{
if (
newIOMessage &&
'mid' in newIOMessage &&
"mid" in newIOMessage &&
!messages.find((msg) => newIOMessage.mid === msg.mid)
) {
if ('author' in newIOMessage) {
if ("author" in newIOMessage) {
newIOMessage.direction =
newIOMessage.author === 'chatbot'
newIOMessage.author === "chatbot"
? Direction.received
: Direction.sent;
newIOMessage.read = true;
@@ -258,8 +258,8 @@ const ChatProvider: React.FC<{
if (
newIOMessage &&
'data' in newIOMessage &&
'quick_replies' in newIOMessage.data
"data" in newIOMessage &&
"quick_replies" in newIOMessage.data
) {
setSuggestions(
(newIOMessage.data.quick_replies || []).map(
@@ -268,7 +268,7 @@ const ChatProvider: React.FC<{
content_type: QuickReplyType.text,
text: qr.title,
payload: qr.payload,
} as ISuggestion),
}) as ISuggestion,
),
);
} else {
@@ -287,11 +287,11 @@ const ChatProvider: React.FC<{
data: TPostMessageEvent;
}) => {
setOutgoingMessageState(
data.type === 'file'
data.type === "file"
? OutgoingMessageState.uploading
: OutgoingMessageState.sending,
);
setMessage('');
setMessage("");
const sentMessage = await socketCtx.socket.post<TMessage>(
`/webhook/${config.channel}/`,
{
@@ -315,7 +315,7 @@ const ChatProvider: React.FC<{
`/webhook/${config.channel}/?first_name=${firstName}&last_name=${lastName}`,
);
localStorage.setItem('profile', JSON.stringify(body.profile));
localStorage.setItem("profile", JSON.stringify(body.profile));
setMessages(
body.messages.map((message) => {
return {
@@ -340,11 +340,11 @@ const ChatProvider: React.FC<{
},
]);
setConnectionState(3);
setScreen('chat');
setScreen("chat");
} catch (e) {
// eslint-disable-next-line no-console
console.error('Unable to subscribe user', e);
setScreen('prechat');
console.error("Unable to subscribe user", e);
setScreen("prechat");
setConnectionState(0);
}
},
@@ -366,9 +366,9 @@ const ChatProvider: React.FC<{
const updateWebviewUrl = (url: string) => {
if (url) {
setWebviewUrl(url);
setScreen('webview');
setScreen("webview");
} else {
setScreen('chat');
setScreen("chat");
}
};
@@ -379,7 +379,7 @@ const ChatProvider: React.FC<{
}, [syncState, isOpen]);
useEffect(() => {
if (screen === 'chat' && connectionState === ConnectionState.connected) {
if (screen === "chat" && connectionState === ConnectionState.connected) {
handleSubscription();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -437,7 +437,7 @@ export const useChat = () => {
const context = useContext(ChatContext);
if (!context) {
throw new Error('useChat must be used within a ChatContext');
throw new Error("useChat must be used within a ChatContext");
}
return context;

View File

@@ -6,14 +6,14 @@
* 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, { createContext, ReactNode, useContext } from 'react';
import React, { createContext, ReactNode, useContext } from "react";
import colors from '../constants/colors';
import { ColorState } from '../types/colors.types';
import colors from "../constants/colors";
import { ColorState } from "../types/colors.types";
import { useSettings } from './SettingsProvider';
import { useSettings } from "./SettingsProvider";
const initialState: ColorState = colors['orange'];
const initialState: ColorState = colors["orange"];
const ColorContext = createContext<{
colors: ColorState;
}>({
@@ -36,7 +36,7 @@ export const useColors = () => {
const context = useContext(ColorContext);
if (!context) {
throw new Error('useColors must be used within a ColorProvider');
throw new Error("useColors must be used within a ColorProvider");
}
return context;

View File

@@ -6,9 +6,9 @@
* 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, { createContext, ReactNode, useContext, useRef } from 'react';
import React, { createContext, ReactNode, useContext, useRef } from "react";
import { DEFAULT_CONFIG } from '../constants/defaultConfig';
import { DEFAULT_CONFIG } from "../constants/defaultConfig";
// Define the type for your config, including all possible properties
export type Config = {
@@ -42,7 +42,7 @@ export const useConfig = () => {
const context = useContext(ConfigContext);
if (!context) {
throw new Error('useConfig must be used within a ConfigProvider');
throw new Error("useConfig must be used within a ConfigProvider");
}
return context;

View File

@@ -6,9 +6,9 @@
* 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, { createContext, ReactNode, useEffect, useState } from 'react';
import React, { createContext, ReactNode, useEffect, useState } from "react";
import { useConfig } from './ConfigProvider';
import { useConfig } from "./ConfigProvider";
const CookieContext = createContext({});
@@ -20,12 +20,12 @@ export const CookieProvider: React.FC<{ children: ReactNode }> = ({
const getCookie = async () => {
try {
await fetch(`${config.apiUrl}/__getcookie`, {
credentials: 'include',
credentials: "include",
});
setInitialized(true);
} catch (e) {
// eslint-disable-next-line no-console
console.warn('Unable to get cookies ...');
console.warn("Unable to get cookies ...");
}
};

View File

@@ -12,13 +12,13 @@ import React, {
useCallback,
useContext,
useState,
} from 'react';
} from "react";
import { useTranslation } from '../hooks/useTranslation';
import { IMenuNode } from '../types/menu.type';
import { SessionStorage } from '../utils/sessionStorage';
import { useTranslation } from "../hooks/useTranslation";
import { IMenuNode } from "../types/menu.type";
import { SessionStorage } from "../utils/sessionStorage";
import { useSubscribe } from './SocketProvider';
import { useSubscribe } from "./SocketProvider";
export type ChannelSettings = {
menu: IMenuNode[];
@@ -65,17 +65,17 @@ const defaultSettings: ChatSettings = {
showTypingIndicator: true,
alwaysScrollToBottom: true,
focusOnOpen: true,
title: 'Hexabot :)',
titleImageUrl: '',
title: "Hexabot :)",
titleImageUrl: "",
inputDisabled: false,
placeholder: 'Write something...',
placeholder: "Write something...",
menu: [],
autoFlush: true,
allowedUploadTypes: ['image/gif', 'image/png', 'image/jpeg'],
allowedUploadTypes: ["image/gif", "image/png", "image/jpeg"],
allowedUploadSize: 2500000,
color: 'blue',
greetingMessage: 'Welcome !',
avatarUrl: '',
color: "blue",
greetingMessage: "Welcome !",
avatarUrl: "",
};
const SettingsContext = createContext<ChatSettings>(defaultSettings);
@@ -87,16 +87,16 @@ export const SettingsProvider: React.FC<ChatSettingsProviderProps> = ({
}) => {
const { t } = useTranslation();
const defaultOrSavedSettings =
SessionStorage.getItem<ChatSettings>('settings');
SessionStorage.getItem<ChatSettings>("settings");
const [settings, setSettingsState] = useState(
defaultOrSavedSettings || defaultSettings,
);
const setSettings = useCallback((settings: ChatSettings) => {
SessionStorage.setItem('settings', settings);
SessionStorage.setItem("settings", settings);
setSettingsState(settings);
}, []);
useSubscribe('settings', (settings: ChannelSettings) => {
useSubscribe("settings", (settings: ChannelSettings) => {
setSettings({
...defaultSettings,
showEmoji: settings.show_emoji,
@@ -105,12 +105,12 @@ export const SettingsProvider: React.FC<ChatSettingsProviderProps> = ({
title: settings.window_title,
titleImageUrl: settings.avatar_url,
menu: settings.menu,
allowedUploadTypes: settings.allowed_upload_types.split(','),
allowedUploadTypes: settings.allowed_upload_types.split(","),
allowedUploadSize: settings.allowed_upload_size,
inputDisabled: settings.input_disabled,
color: settings.theme_color,
greetingMessage: settings.greeting_message,
placeholder: t('settings.placeholder'),
placeholder: t("settings.placeholder"),
avatarUrl: settings.avatar_url,
});
});

View File

@@ -12,11 +12,11 @@ import {
useContext,
useEffect,
useRef,
} from 'react';
} from "react";
import { getSocketIoClient, SocketIoClient } from '../utils/SocketIoClient';
import { getSocketIoClient, SocketIoClient } from "../utils/SocketIoClient";
import { useConfig } from './ConfigProvider';
import { useConfig } from "./ConfigProvider";
interface socketContext {
socket: SocketIoClient;
@@ -33,16 +33,16 @@ export const SocketProvider = (props: PropsWithChildren) => {
onConnect: () => {
// eslint-disable-next-line no-console
console.info(
'Hexabot Live Chat : Successfully established WS Connection!',
"Hexabot Live Chat : Successfully established WS Connection!",
);
},
onConnectError: () => {
// eslint-disable-next-line no-console
console.error('Hexabot Live Chat : Failed to establish WS Connection!');
console.error("Hexabot Live Chat : Failed to establish WS Connection!");
},
onDisconnect: () => {
// eslint-disable-next-line no-console
console.info('Hexabot Live Chat : Disconnected WS.');
console.info("Hexabot Live Chat : Disconnected WS.");
},
}),
);

View File

@@ -6,11 +6,11 @@
* 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, { createContext, useContext, useState, ReactNode } from 'react';
import React, { createContext, useContext, useState, ReactNode } from "react";
import { translations } from '../translations';
import { translations } from "../translations";
import { useConfig } from './ConfigProvider';
import { useConfig } from "./ConfigProvider";
type Language = keyof typeof translations;
@@ -36,7 +36,7 @@ export const TranslationProvider: React.FC<TranslationProviderProps> = ({
const isValidLanguage = (lang: string): lang is Language =>
lang in translations;
const [language, setLanguage] = useState<Language>(
isValidLanguage(initialLanguage) ? initialLanguage : 'en',
isValidLanguage(initialLanguage) ? initialLanguage : "en",
);
return (
@@ -53,7 +53,7 @@ export const useTranslations = () => {
if (!context) {
throw new Error(
'useTranslationContext must be used within a TranslationProvider',
"useTranslationContext must be used within a TranslationProvider",
);
}

View File

@@ -6,9 +6,9 @@
* 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, { createContext, ReactNode, useContext, useState } from 'react';
import React, { createContext, ReactNode, useContext, useState } from "react";
import { ChatScreen } from '../types/state.types';
import { ChatScreen } from "../types/state.types";
export interface WidgetContextType {
syncState: boolean;
@@ -32,7 +32,7 @@ const WidgetProvider: React.FC<{
onOpen,
onClose,
onScrollToTop,
defaultScreen = 'prechat',
defaultScreen = "prechat",
children,
}) => {
const [syncState, setSyncState] = useState<boolean>(true);
@@ -54,9 +54,9 @@ const WidgetProvider: React.FC<{
};
const handleSetScreen = (newScreen: ChatScreen) => {
setScreen(
['prechat', 'postchat', 'webview'].includes(newScreen)
["prechat", "postchat", "webview"].includes(newScreen)
? newScreen
: 'chat',
: "chat",
);
};
const handleSetScroll = (newScroll: number) => {
@@ -87,7 +87,7 @@ export const useWidget = () => {
const context = useContext(WidgetContext);
if (context === undefined) {
throw new Error('useWidget must be used within a WidgetProvider');
throw new Error("useWidget must be used within a WidgetProvider");
}
return context;

View File

@@ -6,8 +6,8 @@
* 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 en from './en/translation.json';
import fr from './fr/translation.json';
import en from "./en/translation.json";
import fr from "./fr/translation.json";
// TypeScript will infer the types automatically here
export const translations = { en, fr } as const;

View File

@@ -7,11 +7,11 @@
*/
export enum StdEventType {
message = 'message',
delivery = 'delivery',
read = 'read',
typing = 'typing',
follow = 'follow',
echo = 'echo',
unknown = '',
message = "message",
delivery = "delivery",
read = "read",
typing = "typing",
follow = "follow",
echo = "echo",
unknown = "",
}

View File

@@ -20,11 +20,11 @@ export type ColorState = {
export type ColorAction = {
type:
| 'setPrimary'
| 'setSecondary'
| 'setText'
| 'setTextSecondary'
| 'updateComponent';
| "setPrimary"
| "setSecondary"
| "setText"
| "setTextSecondary"
| "updateComponent";
payload: {
component: keyof ColorState;
value: { bg: string; text?: string; border?: string };

View File

@@ -13,7 +13,7 @@ export interface IOIncomingMessage<T = unknown> {
}
export interface IOOutgoingMessage<T = unknown> {
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
method: "get" | "post" | "put" | "delete" | "patch" | "options" | "head";
headers: Record<string, string>;
data: T;
// params: Record<string, any>;

View File

@@ -7,9 +7,9 @@
*/
export enum MenuType {
web_url = 'web_url',
postback = 'postback',
nested = 'nested',
web_url = "web_url",
postback = "postback",
nested = "nested",
}
export interface IMenuNode {

View File

@@ -7,13 +7,13 @@
*/
export enum Direction {
sent = 'sent',
received = 'received',
sent = "sent",
received = "received",
}
export enum QuickReplyType {
text = 'text',
location = 'location',
text = "text",
location = "location",
}
export interface IQuickReply {
@@ -31,11 +31,11 @@ export interface IPayload {
}
export enum FileType {
image = 'image',
video = 'video',
audio = 'audio',
file = 'file',
unknkown = 'unknown',
image = "image",
video = "video",
audio = "audio",
file = "file",
unknkown = "unknown",
}
export interface ISubscriber {
@@ -55,8 +55,8 @@ export interface ISubscriber {
}
export enum ButtonType {
postback = 'postback',
web_url = 'web_url',
postback = "postback",
web_url = "web_url",
}
export type TPostBackButton = {
@@ -70,7 +70,7 @@ export type TWebUrlButton = {
title: string;
url: string;
messenger_extensions?: boolean;
webview_height_ratio?: 'compact' | 'tall' | 'full';
webview_height_ratio?: "compact" | "tall" | "full";
};
export type TButton = TPostBackButton | TWebUrlButton;
@@ -92,28 +92,28 @@ export type TRequestSession = {
};
export enum TStatusEventType {
delivery = 'delivery',
read = 'read',
typing = 'typing',
delivery = "delivery",
read = "read",
typing = "typing",
}
export enum TOutgoingMessageType {
text = 'text',
quick_reply = 'quick_reply',
postback = 'postback',
location = 'location',
file = 'file',
text = "text",
quick_reply = "quick_reply",
postback = "postback",
location = "location",
file = "file",
}
export type TEventType = TStatusEventType | TOutgoingMessageType;
export enum IncomingMessageType {
text = 'text',
buttons = 'buttons',
quick_replies = 'quick_replies',
file = 'file',
list = 'list',
carousel = 'carousel',
text = "text",
buttons = "buttons",
quick_replies = "quick_replies",
file = "file",
list = "list",
carousel = "carousel",
}
export type TOutgoingTextMessageData = { text: string };
@@ -213,7 +213,7 @@ export interface IMessageElement {
title: string;
subtitle?: string;
image_url?: string;
default_action?: Omit<TWebUrlButton, 'title'>;
default_action?: Omit<TWebUrlButton, "title">;
buttons?: TButton[];
}
@@ -238,7 +238,7 @@ export type TIncomingCarouselMessageData = {
};
export type TIncomingListMessageData = TIncomingCarouselMessageData & {
top_element_style?: 'large' | 'compact';
top_element_style?: "large" | "compact";
buttons: TButton[];
};

View File

@@ -21,10 +21,10 @@ export enum ConnectionState {
export type ChatScreen =
// Screen that shows up before the chat (user subscription)
| 'prechat'
| "prechat"
// Screen that shows up after the chat is closed (not in use yet)
| 'postchat'
| "postchat"
// Screen shows up when user clicks on a url button where there is a webview
| 'webview'
| "webview"
// Screen that shows the messages and text input
| 'chat';
| "chat";

View File

@@ -6,13 +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).
*/
import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client';
import { io, ManagerOptions, Socket, SocketOptions } from "socket.io-client";
import { Config } from '../providers/ConfigProvider';
import { Config } from "../providers/ConfigProvider";
import {
IOIncomingMessage,
IOOutgoingMessage,
} from '../types/io-message.types';
} from "../types/io-message.types";
type SocketIoClientConfig = Partial<ManagerOptions & SocketOptions>;
@@ -47,7 +47,7 @@ export class SocketIoClient {
addTrailingSlash: true, // eg: https://domain.path/ => https://domain.path/
// autoUnref:false, // firefox only option
// path: "/socket.io", // This is the socket path in the server, leave it as default unless changed manually in server
transports: ['websocket', 'polling'], // ["websocket","polling", "websocket"]
transports: ["websocket", "polling"], // ["websocket","polling", "websocket"]
upgrade: true,
withCredentials: true,
};
@@ -81,9 +81,9 @@ export class SocketIoClient {
onDisconnect,
onConnectError,
}: SocketIoEventHandlers) {
onConnect && this.uniqueOn('connect', onConnect);
onDisconnect && this.uniqueOn('disconnect', onDisconnect);
onConnectError && this.uniqueOn('connect_error', onConnectError);
onConnect && this.uniqueOn("connect", onConnect);
onDisconnect && this.uniqueOn("disconnect", onDisconnect);
onConnectError && this.uniqueOn("connect_error", onConnectError);
}
/**
@@ -139,7 +139,7 @@ export class SocketIoClient {
* @throws Error if the request fails
*/
public async request<T>(
options: Pick<IOOutgoingMessage, 'url' | 'method'> &
options: Pick<IOOutgoingMessage, "url" | "method"> &
Partial<IOOutgoingMessage>,
): Promise<IOIncomingMessage<T>> {
const response: IOIncomingMessage<T> = await this.socket.emitWithAck(
@@ -165,10 +165,10 @@ export class SocketIoClient {
*/
public async get<T>(
url: string,
options?: Partial<Omit<IOOutgoingMessage, 'url' | 'method' | 'body'>>,
options?: Partial<Omit<IOOutgoingMessage, "url" | "method" | "body">>,
): Promise<IOIncomingMessage<T>> {
return this.request({
method: 'get',
method: "get",
url,
...options,
});
@@ -176,10 +176,10 @@ export class SocketIoClient {
public async post<T>(
url: string,
options: Partial<Omit<IOOutgoingMessage, 'url' | 'method'>>,
options: Partial<Omit<IOOutgoingMessage, "url" | "method">>,
): Promise<IOIncomingMessage<T>> {
return this.request({
method: 'post',
method: "post",
url,
...options,
});

View File

@@ -6,14 +6,14 @@
* 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 { FileType } from '../types/message.types';
import { FileType } from "../types/message.types";
export function getFileType(mimeType: string): FileType {
if (mimeType.startsWith('image/')) {
if (mimeType.startsWith("image/")) {
return FileType.image;
} else if (mimeType.startsWith('video/')) {
} else if (mimeType.startsWith("video/")) {
return FileType.video;
} else if (mimeType.startsWith('audio/')) {
} else if (mimeType.startsWith("audio/")) {
return FileType.audio;
} else {
return FileType.file;

View File

@@ -7,8 +7,8 @@
*/
function setItem<T>(key: string, value: T) {
if (typeof value === 'undefined')
throw new Error('Value cannot be undefined');
if (typeof value === "undefined")
throw new Error("Value cannot be undefined");
sessionStorage.setItem(key, JSON.stringify(value));
return true;