Skip to content

Commit

Permalink
Merge pull request #701 from openchatai/feat/handoff-default-componen…
Browse files Browse the repository at this point in the history
…t-and-cleanups

Add a default handoff component
  • Loading branch information
ah7255703 committed Mar 15, 2024
2 parents 6b23adf + ba4f142 commit 24031c1
Show file tree
Hide file tree
Showing 18 changed files with 94 additions and 219 deletions.
5 changes: 4 additions & 1 deletion copilot-widget/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ <h2>
<div id="opencopilot-root"></div>

<script>
const token = "D3BCYwikexJ0hF1Z";
const token = "9Mq2t3kepm1F5Akr";
const apiUrl = "https://api.opencopilot.so/backend";
const socketUrl = "https://api.opencopilot.so";
const sharedConfig = {
Expand All @@ -74,13 +74,16 @@ <h2>
defaultOpen: true, // optional
language: "en", // optional
warnBeforeClose: true,
debug: true,
headers: {
// optional
// Authorization: "Bearer BQAlIe479yo16aXf",
Cookie: `SESSIONID_MYM=pbljd8rnim9pt46d652olit663;`,
},
queryParams: {
// optional
// passing extra query params to the backend along with every user message.
organizationId: 'org_7658431',
},
bot: {
name: "AI Bot",
Expand Down
12 changes: 5 additions & 7 deletions copilot-widget/lib/@components/Fallback.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import type { ComponentProps } from "../contexts/componentRegistery";
type Props = ComponentProps<unknown>;

/**
* The Basic Text component
* The Basic Fallback component (Rendered when Debug is True and the component key is not found)
*/
export function Fallback(props: Props) {
return (
<div className="space-y-2 flex-1">
<div className="w-full max-w-full overflow-auto">
<code dir="auto" className="text-xs leading-tight">
{JSON.stringify(props, null, 1)}
</code>
</div>
<div className="w-full max-w-full overflow-auto shrink-0">
<pre dir="auto" className="text-xs leading-tight">
{JSON.stringify(props, null, 1)}
</pre>
</div>
);
}
12 changes: 12 additions & 0 deletions copilot-widget/lib/@components/Handoff.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ComponentProps, HandoffPayloadType } from "..";

type Props = ComponentProps<HandoffPayloadType>;

export function HandoffComponentRenderer(props: Props) {
return (
<div className="w-full">
<span>{props.data.sentiment}</span>
<span>{props.data.summery}</span>
</div>
);
}
23 changes: 13 additions & 10 deletions copilot-widget/lib/@components/Text.component.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import type { ComponentProps } from "../contexts/componentRegistery";
import { BotMessageWrapper } from "../components/Messages";

type Props = ComponentProps<{
message: string;
Expand All @@ -13,17 +14,19 @@ export function Text({ id, data }: Props) {
const { message } = data;

return (
<div className="space-y-2 flex-1">
<div className=" w-fit">
<div dir="auto">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
className="prose prose-slate font-medium text-sm prose-sm prose-h1:font-medium prose-h2:font-normal prose-headings:my-1 max-w-full"
>
{message}
</ReactMarkdown>
<BotMessageWrapper id={id}>
<div className="space-y-2 flex-1">
<div className=" w-fit">
<div dir="auto">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
className="prose prose-slate font-medium text-sm prose-sm prose-h1:font-medium prose-h2:font-normal prose-headings:my-1 max-w-full"
>
{message}
</ReactMarkdown>
</div>
</div>
</div>
</div>
</BotMessageWrapper>
);
}
47 changes: 8 additions & 39 deletions copilot-widget/lib/components/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useWidgetState } from "../contexts/WidgetState";
import { AlertTriangle, CircleDashed, X } from "lucide-react";
import { AlertTriangle, X } from "lucide-react";
import {
Dialog,
DialogClose,
Expand All @@ -11,7 +11,6 @@ import { Button } from "./Button";
import { useInitialData } from "@lib/hooks/useInitialData";
import { useLang } from "@lib/contexts/LocalesProvider";
import { useConfigData } from "@lib/contexts/ConfigData.tsx";
import { useSocket } from "@lib/contexts/SocketProvider";

function WarnBeforeCloseDialog({ onClose }: { onClose: () => void }) {
const { get } = useLang();
Expand Down Expand Up @@ -42,41 +41,12 @@ function WarnBeforeCloseDialog({ onClose }: { onClose: () => void }) {
</Dialog>
);
}
function DisconnedtedAlert() {
const { state } = useSocket();
if (state.state === "disconnected") {
return (
<div className="bg-rose-200 p-1 px-2 text-rose-500 flex items-start gap-2">
<AlertTriangle className="text-xl size-[1em] text-rose-800" />
<span>
The connection to the server has been lost. Please wait while we try
to reconnect.
</span>
</div>
);
}
return null;
}

function ConnectingAlert() {
const { state } = useSocket();
if (state.state === "retrying") {
return (
<div className="bg-yellow-200 p-1 px-2 text-yellow-500 flex items-start gap-2">
<CircleDashed className="text-xl size-[1em] animate-spin text-yellow-800" />
<span>
We are trying to reconnect to the server. Please wait a moment.
</span>
</div>
);
}
return null;
}

export default function ChatHeader() {
const [, , SetState] = useWidgetState();
const { data } = useInitialData();
const config = useConfigData();
const debug = config.debug ?? false;

const onClose = () => {
SetState(false);
Expand All @@ -89,9 +59,12 @@ export default function ChatHeader() {
return (
<header className="border-b border-b-black/10 w-full">
<div className="p-2 w-full flex items-center justify-between">
<h1 className="font-semibold text-sm">
{data?.bot_name || "OpenCopilot"}
</h1>
<div className="flex items-center gap-2">
<h1 className="font-semibold text-sm">
{data?.bot_name || "OpenCopilot"}
</h1>
{debug && <span className="py-0.5 px-1 bg-primary font-semibold text-[10px] rounded-lg text-white">DEBUG</span>}
</div>
<div className="flex items-center gap-3">
{config?.warnBeforeClose === false ? (
<button onClick={onClose}>
Expand All @@ -102,10 +75,6 @@ export default function ChatHeader() {
)}
</div>
</div>
<div className="alerts w-full text-xs">
<DisconnedtedAlert />
<ConnectingAlert />
</div>
</header>
);
}
12 changes: 1 addition & 11 deletions copilot-widget/lib/components/Messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { formatTimeFromTimestamp } from "../utils/time";
import { getLast, isEmpty } from "@lib/utils/utils";
import { useConfigData } from "@lib/contexts/ConfigData";
import { useChatState } from "@lib/contexts/statefulMessageHandler";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ToolTip";

export function BotIcon({ error }: { error?: boolean }) {
const config = useConfigData();
Expand Down Expand Up @@ -48,16 +47,7 @@ function UserAvatar() {
function User() {
const config = useConfigData();

return (
<Tooltip>
<TooltipContent hidden={!config?.user} side="top" align="center">
<span>{config?.user?.name}</span>
</TooltipContent>
<TooltipTrigger asChild>
<UserAvatar />
</TooltipTrigger>
</Tooltip>
);
return <UserAvatar />;
}

export function BotMessageWrapper({
Expand Down
42 changes: 0 additions & 42 deletions copilot-widget/lib/components/ToolTip.tsx

This file was deleted.

34 changes: 13 additions & 21 deletions copilot-widget/lib/components/VoiceRecorder.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MicIcon, Square } from "lucide-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ToolTip";
import { useAxiosInstance } from "@lib/contexts/axiosInstance";
import { now } from "@lib/utils/time";
import { useEffect } from "react";
Expand Down Expand Up @@ -53,25 +52,18 @@ export function VoiceRecorder({
}

return (
<Tooltip open={isRecording}>
<TooltipContent sideOffset={5} side="top">
{get("recording")} {recordingTime}s
</TooltipContent>
<TooltipTrigger asChild>
<button
onClick={handleClick}
className={cn(
"flex items-center justify-center shrink-0 rounded-full size-6 [&>svg]:size-4",
disabled ? "bg-rose-500" : "bg-emerald-500"
)}
>
{isRecording ? (
<Square strokeLinecap="round" className="text-accent size-[1em]" />
) : (
<MicIcon strokeLinecap="round" className="text-white size-[1em]" />
)}
</button>
</TooltipTrigger>
</Tooltip>
<button
onClick={handleClick}
className={cn(
"flex items-center justify-center shrink-0 rounded-full size-6 [&>svg]:size-4",
disabled ? "bg-rose-500" : "bg-emerald-500"
)}
>
{isRecording ? (
<Square strokeLinecap="round" className="text-accent size-[1em]" />
) : (
<MicIcon strokeLinecap="round" className="text-white size-[1em]" />
)}
</button>
);
}
9 changes: 8 additions & 1 deletion copilot-widget/lib/contexts/componentRegistery.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from "react";
import type { BotMessageType } from "./messageHandler";

import { Fallback } from "@lib/@components/Fallback.component";
import { FormComponent } from "@lib/@components/Form.component";
import { Loading } from "@lib/@components/Loading.component";
import { Text } from "@lib/@components/Text.component";
import type { BotMessageType } from "./messageHandler";
import { HandoffComponentRenderer } from "@lib/@components/Handoff.component";

export type ComponentProps<TData> = BotMessageType<TData>;

Expand All @@ -15,6 +17,7 @@ export type ComponentType = {
type OptionsType = {
components?: ComponentType[];
};

/**
* this a singleton class helps me to easily control the components present/available in the widget.
* it also manges the various types of components to be added along the way.
Expand All @@ -37,6 +40,10 @@ export class ComponentRegistery {
key: "LOADING",
component: Loading,
},
{
key: "HANDOFF",
component: HandoffComponentRenderer,
},
] as const;

constructor(opts: OptionsType) {
Expand Down
1 change: 1 addition & 0 deletions copilot-widget/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { BotMessageWrapper } from "./components/Messages";
export { default as Root } from "./Root";
export { CopilotWidget } from "./CopilotWidget";
export type {
Expand Down
19 changes: 7 additions & 12 deletions copilot-widget/lib/screens/ChatScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import ChatHeader from "../components/ChatHeader";
import { ComponentType, useEffect, useRef } from "react";
import {
BotMessageWrapper,
BotInitialMessage,
UserMessage,
} from "../components/Messages";
import { BotInitialMessage, UserMessage } from "../components/Messages";
import useScrollToPercentage from "../hooks/useScrollTo";
import ChatInputFooter from "../components/ChatInputFooter";
import { useConfigData } from "../contexts/ConfigData";
Expand Down Expand Up @@ -61,13 +57,12 @@ export default function ChatScreen() {
}>;

return (
<BotMessageWrapper id={message.id} key={index}>
<Component
{...message}
data={message.data ?? {}}
id={message.id}
/>
</BotMessageWrapper>
<Component
{...message}
data={message.data ?? {}}
id={message.id}
key={index}
/>
);
} else if (message.from === "user") {
return (
Expand Down
12 changes: 10 additions & 2 deletions copilot-widget/lib/utils/createSocket.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { io } from "socket.io-client";
import { Socket, io } from "socket.io-client";

interface ConfigType {
socketUrl: string;
token: string;
sessionId: string;
}

const sockets = new Map<string, Socket>();

export function createSocketClient({
socketUrl,
token,
sessionId,
}: ConfigType) {
return io(socketUrl, {
const socket = sockets.get(sessionId + token);
if (socket) {
return socket;
}
const newSocket = io(socketUrl, {
autoConnect: false,
transports: ["websocket"],
extraHeaders: {
"X-Bot-Token": token,
"X-Session-Id": sessionId,
},
});
sockets.set(sessionId + token, newSocket);
return newSocket;
}
Loading

0 comments on commit 24031c1

Please sign in to comment.