Skip to content

Commit

Permalink
chore(ai): add timestamp function to displayText (#5782)
Browse files Browse the repository at this point in the history
* chore(ai): add timestamp function to displayText

* Create heavy-bottles-poke.md

* move DisplayTextProvider below static providers, revert context exports

* add types git date message override
  • Loading branch information
thaddmt authored Sep 11, 2024
1 parent 9b230f1 commit c485f10
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-bottles-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@aws-amplify/ui-react-ai": patch
---

chore(ai): add timestamp function to displayText
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ const { useAIConversation } = createAIHooks(client);

Amplify.configure(outputs);

const formatDate = (date: Date): string =>
`Argh the time be round ${date.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: 'numeric',
hour12: true,
})}`;

function Chat() {
const [
{
Expand All @@ -25,6 +32,7 @@ function Chat() {
return (
<Card variation="outlined" width="50%" height="300px" margin="0 auto">
<AIConversation
displayText={{ getMessageTimestampText: formatDate }}
messages={messages}
handleSendMessage={sendMessage}
isLoading={isLoading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function AIConversationBase({
suggestedPrompts,
variant,
isLoading,
displayText,
}: AIConversationBaseProps): JSX.Element {
const icons = useIcons('aiConversation');
const defaultAvatars: Avatars = {
Expand Down Expand Up @@ -60,6 +61,7 @@ function AIConversationBase({
Form,
...controls,
},
displayText,
});

const providerProps = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`displayText should match snapshot 1`] = `
[
"getMessageTimestampText",
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defaultAIConversationDisplayTextEn } from '../displayText';

describe('displayText', () => {
it('should match snapshot', () => {
const sortedDisplayText = Object.keys(
defaultAIConversationDisplayTextEn
).sort();

expect(sortedDisplayText).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createContextUtilities } from '@aws-amplify/ui-react-core';
import {
ConversationDisplayText,
defaultAIConversationDisplayTextEn,
} from '../displayText';

export const {
ConversationDisplayTextContext,
ConversationDisplayTextProvider,
useConversationDisplayText,
} = createContextUtilities<Required<ConversationDisplayText>>({
contextName: 'ConversationDisplayText',
defaultValue: defaultAIConversationDisplayTextEn,
});
38 changes: 28 additions & 10 deletions packages/react-ai/src/components/AIConversation/context/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
import { ActionsContext } from './ActionsContext';
import { AvatarsContext } from './AvatarsContext';
import { ConversationInputContext } from './ConversationInputContext';
import { MessagesContext, RoleContext } from './MessagesContext';
import { SuggestedPromptsContext } from './SuggestedPromptsContext';
import { MessageVariantContext } from './MessageVariantContext';

export { ActionsContext, ActionsProvider } from './ActionsContext';
export { AvatarsContext, AvatarsProvider } from './AvatarsContext';
export {
ActionsContext,
AvatarsContext,
ConversationInputContext,
ConversationInput,
ConversationInputContextProvider,
} from './ConversationInputContext';
export {
MessagesContext,
RoleContext,
MessagesProvider,
} from './MessagesContext';
export {
SuggestedPromptsContext,
SuggestedPromptProvider,
} from './SuggestedPromptsContext';
export {
MessageVariantContext,
};
MessageVariantProvider,
} from './MessageVariantContext';
export {
ConversationDisplayTextContext,
useConversationDisplayText,
ConversationDisplayTextProvider,
} from './DisplayTextContext';
export {
ControlsContext,
ControlsContextProps,
ControlsProvider,
} from './ControlsContext';
export { LoadingContextProvider } from './LoadingContext';
export { ResponseComponentsProvider } from './ResponseComponentsContext';
export { SendMessageContextProvider } from './SendMessageContext';

export * from './elements';
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function createAIConversation(input: AIConversationInput = {}): {
responseComponents,
variant,
controls,
displayText,
} = input;

const Provider = createProvider({
Expand All @@ -38,6 +39,7 @@ export function createAIConversation(input: AIConversationInput = {}): {
responseComponents,
variant,
controls,
displayText,
});

function AIConversation(props: AIConversationProps): JSX.Element {
Expand Down
66 changes: 39 additions & 27 deletions packages/react-ai/src/components/AIConversation/createProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ import React from 'react';

import { ElementsProvider } from '@aws-amplify/ui-react-core/elements';

import { ActionsProvider } from './context/ActionsContext';
import { AvatarsProvider } from './context/AvatarsContext';
import { ConversationInputContextProvider } from './context/ConversationInputContext';
import { MessagesProvider } from './context/MessagesContext';
import { MessageVariantProvider } from './context/MessageVariantContext';
import { SuggestedPromptProvider } from './context/SuggestedPromptsContext';
import { AIConversationInput, AIConversationProps } from './types';
import { ResponseComponentsProvider } from './context/ResponseComponentsContext';
import { SendMessageContextProvider } from './context/SendMessageContext';
import { ControlsProvider } from './context/ControlsContext';
import { LoadingContextProvider } from './context/LoadingContext';
import { defaultAIConversationDisplayTextEn } from './displayText';
import {
ConversationDisplayTextProvider,
SuggestedPromptProvider,
ConversationInputContextProvider,
AvatarsProvider,
ActionsProvider,
MessageVariantProvider,
MessagesProvider,
ControlsProvider,
LoadingContextProvider,
ResponseComponentsProvider,
SendMessageContextProvider,
} from './context';

export default function createProvider({
elements,
Expand All @@ -21,6 +25,7 @@ export default function createProvider({
responseComponents,
variant,
controls,
displayText,
}: Pick<
AIConversationInput,
| 'elements'
Expand All @@ -29,6 +34,7 @@ export default function createProvider({
| 'responseComponents'
| 'variant'
| 'controls'
| 'displayText'
>) {
return function Provider({
children,
Expand All @@ -42,28 +48,34 @@ export default function createProvider({
AIConversationProps,
'messages' | 'avatars' | 'handleSendMessage' | 'isLoading'
>): React.JSX.Element {
const _displayText = {
...defaultAIConversationDisplayTextEn,
...displayText,
};
return (
<ElementsProvider elements={elements}>
<ControlsProvider controls={controls}>
<SuggestedPromptProvider suggestedPrompts={suggestedPrompts}>
<ResponseComponentsProvider responseComponents={responseComponents}>
<ConversationInputContextProvider>
<SendMessageContextProvider
handleSendMessage={handleSendMessage}
>
<AvatarsProvider avatars={avatars}>
<ActionsProvider actions={actions}>
<MessageVariantProvider variant={variant}>
<MessagesProvider messages={messages}>
<LoadingContextProvider isLoading={isLoading}>
{children}
</LoadingContextProvider>
</MessagesProvider>
</MessageVariantProvider>
</ActionsProvider>
</AvatarsProvider>
</SendMessageContextProvider>
</ConversationInputContextProvider>
<ConversationDisplayTextProvider {..._displayText}>
<ConversationInputContextProvider>
<SendMessageContextProvider
handleSendMessage={handleSendMessage}
>
<AvatarsProvider avatars={avatars}>
<ActionsProvider actions={actions}>
<MessageVariantProvider variant={variant}>
<MessagesProvider messages={messages}>
<LoadingContextProvider isLoading={isLoading}>
{children}
</LoadingContextProvider>
</MessagesProvider>
</MessageVariantProvider>
</ActionsProvider>
</AvatarsProvider>
</SendMessageContextProvider>
</ConversationInputContextProvider>
</ConversationDisplayTextProvider>
</ResponseComponentsProvider>
</SuggestedPromptProvider>
</ControlsProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { DisplayTextTemplate } from '@aws-amplify/ui';
import { formatDate } from './utils';

export type ConversationDisplayText = {
conversationHeaderText?: string;
getMessageTimestampText?: (date: Date) => string;
};

export const defaultAIConversationDisplayText: Required<AIConversationDisplayText> =
export const defaultAIConversationDisplayTextEn: Required<AIConversationDisplayText> =
{
conversationHeaderText: 'Raven Chat',
getMessageTimestampText: (date: Date) => formatDate(date),
};

export type AIConversationDisplayText =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {
MessagesContext,
MessageVariantContext,
RoleContext,
useConversationDisplayText,
} from '../../context';
import { AIConversationElements } from '../../context/elements';
import { convertBufferToBase64, formatDate } from '../../utils';
import { convertBufferToBase64 } from '../../utils';
import { ActionsBarControl } from './ActionsBarControl';
import { AvatarControl } from './AvatarControl';
import { ConversationMessage } from '../../../../types';
Expand Down Expand Up @@ -159,6 +160,7 @@ const Layout: typeof View = React.forwardRef(function Layout(props, ref) {
export const MessagesControl: MessagesControl = ({ renderMessage }) => {
const messages = React.useContext(MessagesContext);
const controls = React.useContext(ControlsContext);
const { getMessageTimestampText } = useConversationDisplayText();
const messagesRef = React.useRef<(HTMLDivElement | null)[]>([]);

const [focusedItemIndex, setFocusedItemIndex] = React.useState(
Expand Down Expand Up @@ -222,7 +224,9 @@ export const MessagesControl: MessagesControl = ({ renderMessage }) => {
<HeaderContainer>
<AvatarControl />
<Separator />
<Timestamp>{formatDate(new Date(message.createdAt))}</Timestamp>
<Timestamp>
{getMessageTimestampText(new Date(message.createdAt))}
</Timestamp>
</HeaderContainer>
<MessageControl message={message} />
{message.role === 'assistant' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
AvatarsContext,
MessageVariantContext,
RoleContext,
useConversationDisplayText,
} from '../../context';
import { ConversationMessage } from '../../../../types';
import { formatDate } from '../../utils';
import { ControlsContextProps } from '../../context/ControlsContext';
import {
ComponentClassName,
Expand All @@ -19,6 +19,7 @@ const MessageMeta = ({ message }: { message: ConversationMessage }) => {
// need to pass this in as props in order for it to be overridable
const avatars = React.useContext(AvatarsContext);
const role = React.useContext(RoleContext);
const { getMessageTimestampText } = useConversationDisplayText();
// maybe rename 'avatar' to something else
const avatar = role === 'assistant' ? avatars?.ai : avatars?.user;
return (
Expand All @@ -27,7 +28,7 @@ const MessageMeta = ({ message }: { message: ConversationMessage }) => {
{avatar?.username}
</Text>
<Text className={ComponentClassName.AIConversationMessageSenderTimestamp}>
{formatDate(new Date(message.createdAt))}
{getMessageTimestampText(new Date(message.createdAt))}
</Text>
</View>
);
Expand Down

0 comments on commit c485f10

Please sign in to comment.