diff --git a/.changeset/heavy-bottles-poke.md b/.changeset/heavy-bottles-poke.md new file mode 100644 index 00000000000..4aeda4cac1e --- /dev/null +++ b/.changeset/heavy-bottles-poke.md @@ -0,0 +1,5 @@ +--- +"@aws-amplify/ui-react-ai": patch +--- + +chore(ai): add timestamp function to displayText diff --git a/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx b/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx index f64dc5299a8..b5c9e83fd3f 100644 --- a/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx +++ b/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx @@ -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 [ { @@ -25,6 +32,7 @@ function Chat() { return ( { + it('should match snapshot', () => { + const sortedDisplayText = Object.keys( + defaultAIConversationDisplayTextEn + ).sort(); + + expect(sortedDisplayText).toMatchSnapshot(); + }); +}); diff --git a/packages/react-ai/src/components/AIConversation/context/DisplayTextContext.tsx b/packages/react-ai/src/components/AIConversation/context/DisplayTextContext.tsx new file mode 100644 index 00000000000..26ee4f5621f --- /dev/null +++ b/packages/react-ai/src/components/AIConversation/context/DisplayTextContext.tsx @@ -0,0 +1,14 @@ +import { createContextUtilities } from '@aws-amplify/ui-react-core'; +import { + ConversationDisplayText, + defaultAIConversationDisplayTextEn, +} from '../displayText'; + +export const { + ConversationDisplayTextContext, + ConversationDisplayTextProvider, + useConversationDisplayText, +} = createContextUtilities>({ + contextName: 'ConversationDisplayText', + defaultValue: defaultAIConversationDisplayTextEn, +}); diff --git a/packages/react-ai/src/components/AIConversation/context/index.ts b/packages/react-ai/src/components/AIConversation/context/index.ts index e8ca576a8ec..edbfc960bda 100644 --- a/packages/react-ai/src/components/AIConversation/context/index.ts +++ b/packages/react-ai/src/components/AIConversation/context/index.ts @@ -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'; diff --git a/packages/react-ai/src/components/AIConversation/createAIConversation.tsx b/packages/react-ai/src/components/AIConversation/createAIConversation.tsx index 6f6dda89e77..58789d2bc22 100644 --- a/packages/react-ai/src/components/AIConversation/createAIConversation.tsx +++ b/packages/react-ai/src/components/AIConversation/createAIConversation.tsx @@ -29,6 +29,7 @@ export function createAIConversation(input: AIConversationInput = {}): { responseComponents, variant, controls, + displayText, } = input; const Provider = createProvider({ @@ -38,6 +39,7 @@ export function createAIConversation(input: AIConversationInput = {}): { responseComponents, variant, controls, + displayText, }); function AIConversation(props: AIConversationProps): JSX.Element { diff --git a/packages/react-ai/src/components/AIConversation/createProvider.tsx b/packages/react-ai/src/components/AIConversation/createProvider.tsx index acb34ab9527..cede94d9dfc 100644 --- a/packages/react-ai/src/components/AIConversation/createProvider.tsx +++ b/packages/react-ai/src/components/AIConversation/createProvider.tsx @@ -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, @@ -21,6 +25,7 @@ export default function createProvider({ responseComponents, variant, controls, + displayText, }: Pick< AIConversationInput, | 'elements' @@ -29,6 +34,7 @@ export default function createProvider({ | 'responseComponents' | 'variant' | 'controls' + | 'displayText' >) { return function Provider({ children, @@ -42,28 +48,34 @@ export default function createProvider({ AIConversationProps, 'messages' | 'avatars' | 'handleSendMessage' | 'isLoading' >): React.JSX.Element { + const _displayText = { + ...defaultAIConversationDisplayTextEn, + ...displayText, + }; return ( - - - - - - - - {children} - - - - - - - + + + + + + + + + {children} + + + + + + + + diff --git a/packages/react-ai/src/components/AIConversation/displayText.ts b/packages/react-ai/src/components/AIConversation/displayText.ts index 89cc0b8cbaa..20737b5603a 100644 --- a/packages/react-ai/src/components/AIConversation/displayText.ts +++ b/packages/react-ai/src/components/AIConversation/displayText.ts @@ -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 = +export const defaultAIConversationDisplayTextEn: Required = { - conversationHeaderText: 'Raven Chat', + getMessageTimestampText: (date: Date) => formatDate(date), }; export type AIConversationDisplayText = diff --git a/packages/react-ai/src/components/AIConversation/views/Controls/MessagesControl.tsx b/packages/react-ai/src/components/AIConversation/views/Controls/MessagesControl.tsx index 40f5fb2a76d..05232f7da2c 100644 --- a/packages/react-ai/src/components/AIConversation/views/Controls/MessagesControl.tsx +++ b/packages/react-ai/src/components/AIConversation/views/Controls/MessagesControl.tsx @@ -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'; @@ -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( @@ -222,7 +224,9 @@ export const MessagesControl: MessagesControl = ({ renderMessage }) => { - {formatDate(new Date(message.createdAt))} + + {getMessageTimestampText(new Date(message.createdAt))} + {message.role === 'assistant' ? ( diff --git a/packages/react-ai/src/components/AIConversation/views/default/MessageList.tsx b/packages/react-ai/src/components/AIConversation/views/default/MessageList.tsx index 28900d24f07..015b5dcbc5e 100644 --- a/packages/react-ai/src/components/AIConversation/views/default/MessageList.tsx +++ b/packages/react-ai/src/components/AIConversation/views/default/MessageList.tsx @@ -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, @@ -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 ( @@ -27,7 +28,7 @@ const MessageMeta = ({ message }: { message: ConversationMessage }) => { {avatar?.username} - {formatDate(new Date(message.createdAt))} + {getMessageTimestampText(new Date(message.createdAt))} );