Skip to content

Commit

Permalink
feat: working on UserResponse component (COR-0000) (#253)
Browse files Browse the repository at this point in the history
Co-authored-by: Mikaal Naik <[email protected]>
  • Loading branch information
gillyb and mikaalnaik authored Oct 28, 2024
1 parent 617d4e2 commit 5026d2f
Show file tree
Hide file tree
Showing 16 changed files with 160 additions and 88 deletions.
2 changes: 2 additions & 0 deletions packages/chat/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export { default as microphone } from './microphone.svg?react';
export { default as minus } from './minus.svg?react';
export { default as reset } from './reset.svg?react';
export { default as smallArrowUp } from './small-arrow-up.svg?react';
export { default as sound } from './sound.svg?react';
export { default as soundOff } from './sound-off.svg?react';
export { default as stop } from './stop.svg?react';
export { default as thumbsUp } from './thumbs-up.svg?react';
export { default as topCaret } from './top-caret.svg?react';
Expand Down
7 changes: 7 additions & 0 deletions packages/chat/src/assets/svg/sound-off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/chat/src/assets/svg/sound.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions packages/chat/src/components/NewChat/NewChat.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import type { Meta, StoryObj } from '@storybook/react';
import { assignInlineVars } from '@vanilla-extract/dynamic';

import EMPTY_IMAGE from '@/__fixtures__/empty-image.png';
import { DEFAULT_AVATAR, SystemResponse, UserMessage } from '@/main';
import { DEFAULT_AVATAR, SystemResponse } from '@/main';
import { WithDefaultPalette } from '@/storybook/decorators';
import { createPalette } from '@/styles/colors';
import { PALETTE } from '@/styles/colors.css';

import { UserResponse } from '../UserResponse';
import { NewChat } from '.';

const meta: Meta = {
Expand All @@ -29,7 +30,7 @@ const AgentSays = (messages: string[]) => (
messages={messages.map((m) => ({ type: 'text', text: m }))}
/>
);
const UserSays = (text: string) => <UserMessage message={text} />;
const UserSays = (text: string) => <UserResponse message={text} timestamp={Date.now()} />;

export const Base = {
render: () => (
Expand Down
49 changes: 46 additions & 3 deletions packages/chat/src/components/NewChat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import clsx from 'clsx';
import { useRef, useState } from 'react';
import { useContext, useMemo, useRef, useState } from 'react';

import { ClassName } from '@/constants';
import { RuntimeStateAPIContext, RuntimeStateContext } from '@/contexts';
import { RenderMode } from '@/dtos/RenderOptions.dto';
import type { Nullish } from '@/types';
import { chain } from '@/utils/functional';

import mockAvatar from '../../assets/blank-image.png';
import { Header, type HeaderProps } from '../Header';
import { Header, type HeaderActionProps, type HeaderProps } from '../Header';
import { type INewFooter, NewFooter } from '../NewFooter';
import { Prompt } from '../Prompt';
import { ScrollToBottom } from '../ScrollToBottom';
import { type IWelcomeMessage, WelcomeMessage } from '../WelcomeMessage';
import { chatContainer, dialogContainer } from './NewChat.css';
Expand Down Expand Up @@ -49,9 +53,43 @@ export const NewChat: React.FC<INewChat> = ({
extraLinkText,
extraLinkUrl,
children,
onEnd,
onMinimize,
audioInterface,
}) => {
// const [chatMessages, setChatMessages] = useState(messages ?? []);
const [newMessage, setNewMessage] = useState('');
const [hasAlert, setAlert] = useState(false);

const { config, toggleAudioOutput } = useContext(RuntimeStateAPIContext);
const state = useContext(RuntimeStateContext);

const handleClose = (event: React.MouseEvent<HTMLButtonElement>): void => {
if (hasEnded) {
onEnd?.(event);
} else {
setAlert(true);
}
};

const handleResume = (): void => setAlert(false);

const headerActions = useMemo<HeaderActionProps[]>(() => {
const items: HeaderActionProps[] = [{ svg: 'close', onClick: handleClose }];

if (config.render?.mode === RenderMode.OVERLAY) {
items.unshift({ svg: 'minus', onClick: onMinimize });
}

if (audioInterface) {
items.unshift({
svg: state.audioOutput ? 'sound' : 'soundOff',
onClick: toggleAudioOutput,
});
}

return items;
}, [config.render, handleClose, onMinimize, state.audioOutput, audioInterface]);

const scrollableAreaRef = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -79,7 +117,7 @@ export const NewChat: React.FC<INewChat> = ({

return (
<div className={clsx(ClassName.CHAT, chatContainer)} onKeyDown={handleKeyDown}>
<Header title={title} image={mockAvatar} />
<Header title={title} image={mockAvatar} actions={headerActions} />
<div ref={scrollableAreaRef} className={dialogContainer}>
<WelcomeMessage title={title} description={description} avatar={avatar} />
{children}
Expand All @@ -93,6 +131,11 @@ export const NewChat: React.FC<INewChat> = ({
extraLinkUrl={extraLinkUrl}
messageInputProps={{ ...messageInputProps }}
/>
<Prompt
visible={hasAlert}
accept={{ label: 'End Chat', /* type: 'warn', */ onClick: chain(onEnd, handleResume) }}
cancel={{ label: 'Cancel', onClick: handleResume }}
/>
</div>
);
};
5 changes: 4 additions & 1 deletion packages/chat/src/components/Prompt/styles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { recipe } from '@vanilla-extract/recipes';

import { COLORS } from '@/styles/colors';
import { SIZES } from '@/styles/sizes';
import { transition } from '@/styles/transitions';

const PROMPT_OVERFLOW = 10;

Expand Down Expand Up @@ -37,18 +38,20 @@ export const promptContainer = recipe({

export const chatOverlay = recipe({
base: {
display: 'none',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0,0,0,0.12)',
opacity: 0,
transition: 'opacity',
transition: transition(['opacity']),
},
variants: {
visible: {
true: {
display: 'block',
opacity: 1,
zIndex: 2,
pointerEvents: 'auto',
Expand Down
20 changes: 16 additions & 4 deletions packages/chat/src/components/SystemResponse/SystemMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Image } from '../Image';
import { MessageType } from './constants';
import { ExtensionMessage } from './ExtensionMessage';
import EndState from './state/end';
import { hide, messageContainer, systemMessageContainer } from './styles.css';
import { hide, messageContainer, responseAvatar, systemMessageContainer } from './styles.css';
import type { MessageProps } from './types';

export interface SystemMessageProps extends React.PropsWithChildren {
Expand Down Expand Up @@ -45,21 +45,33 @@ export interface SystemMessageProps extends React.PropsWithChildren {
* @default false
*/
feedback?: IFeedbackButton | undefined;

/**
* If this is the first message in a group of messages
*/
first?: boolean;
}

/**
* An individual message within a system response.
*/
export const SystemMessage: React.FC<SystemMessageProps> = ({ avatar, feedback, message, withImage, children }) => {
export const SystemMessage: React.FC<SystemMessageProps> = ({
avatar,
feedback,
message,
withImage,
first,
children,
}) => {
const { config } = useContext(RuntimeStateAPIContext);

if (!children && message?.type === MessageType.END) {
return <EndState />;
}

return (
<div className={clsx(ClassName.SYSTEM_RESPONSE, systemMessageContainer())}>
<Avatar avatar={avatar} className={clsx(withImage ? '' : hide)} />
<div className={clsx(ClassName.SYSTEM_RESPONSE, systemMessageContainer({ first }))}>
<Avatar avatar={avatar} className={clsx(withImage ? '' : hide, responseAvatar)} />
<div className={messageContainer}>
{children ??
match(message)
Expand Down
1 change: 1 addition & 0 deletions packages/chat/src/components/SystemResponse/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const SystemResponse: React.FC<SystemResponseProps> = ({
feedback={complete && !showIndicator && index === visibleMessages.length - 1 ? feedback : undefined}
avatar={avatar}
timestamp={timestamp}
first={index === 0}
key={index}
/>
))}
Expand Down
14 changes: 12 additions & 2 deletions packages/chat/src/components/SystemResponse/styles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@ export const hide = style({
export const systemMessageContainer = recipe({
base: {
display: 'flex',
alignItems: 'center',
marginBottom: 16,
alignItems: 'flex-end',
},
variants: {
first: {
true: {
marginTop: 12,
},
},
},
});

export const responseAvatar = style({
marginBottom: 4,
});

export const messageContainer = style({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const messageContainer = style({
color: COLORS.ACCENT[50],
padding: '11px 16px 10px',
marginBottom: 4,
marginTop: 12,
fontFamily: FAMILY,
position: 'relative',
fontSize: '14px',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';

import UserResponse from '.';
import { UserResponse } from '.';

type Story = StoryObj<typeof UserResponse>;
const meta: Meta<typeof UserResponse> = {
Expand Down
42 changes: 18 additions & 24 deletions packages/chat/src/components/UserResponse/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useContext } from 'react';
import clsx from 'clsx';

import Message from '@/components/Message';
import Tooltip from '@/components/Tooltip';
import { RuntimeStateAPIContext } from '@/contexts';
import { ClassName } from '@/constants';
import { useAutoScroll } from '@/hooks';

import { Container, Debug, Row } from './styled';
import { debugMessage, messageContainer, messageRow, messageStyle } from './styles.css';

export interface DebugActionProps {
label: string;
Expand Down Expand Up @@ -35,37 +34,32 @@ export interface UserResponseProps {
debug?: DebugResponseProps;
}

const UserResponse: React.FC<UserResponseProps> = ({ message, debug }) => {
/**
* A user-sent text response.
*
* @see {@link https://voiceflow.github.io/react-chat/?path=/story/components-chat-userresponse--simple}
*/
export const UserResponse: React.FC<UserResponseProps> = ({ message, debug }) => {
useAutoScroll();

const { config } = useContext(RuntimeStateAPIContext);
// TODO: Check this in different render modes
// const { config } = useContext(RuntimeStateAPIContext);

return (
<Container mode={config.render.mode}>
<Row>
{/* <Timestamp value={timestamp} /> */}
<Message from="user">{message}</Message>
</Row>
<div className={clsx(ClassName.USER_RESPONSE, messageContainer)}>
<div className={messageRow}>
<div className={messageStyle}>{message}</div>
</div>
{debug && (
<>
<Debug>{debug.message}</Debug>
<aside className={debugMessage}>{debug.message}</aside>
{debug.reason && (
<Tooltip label={debug.action?.label} onClick={debug.action?.onClick} orientation="right">
{debug.reason}
</Tooltip>
)}
</>
)}
</Container>
</div>
);
};

/**
* A user-sent text response.
*
* @see {@link https://voiceflow.github.io/react-chat/?path=/story/components-chat-userresponse--simple}
*/
export default Object.assign(UserResponse, {
Container,
Debug,
Row,
});
49 changes: 0 additions & 49 deletions packages/chat/src/components/UserResponse/styled.ts

This file was deleted.

Loading

0 comments on commit 5026d2f

Please sign in to comment.