|
1 | 1 | import * as React from 'react'; |
2 | | -import { useMemo } from 'react'; |
3 | | -import type { MessageFormatter, ReceivedChatMessage } from '@livekit/components-react'; |
4 | 2 | import { cn } from '@/lib/utils'; |
5 | 3 |
|
6 | 4 | export interface ChatEntryProps extends React.HTMLAttributes<HTMLLIElement> { |
7 | | - /** The chat massage object to display. */ |
8 | | - entry: ReceivedChatMessage; |
9 | | - /** Hide sender name. Useful when displaying multiple consecutive chat messages from the same person. */ |
10 | | - hideName?: boolean; |
11 | | - /** Hide message timestamp. */ |
12 | | - hideTimestamp?: boolean; |
13 | | - /** An optional formatter for the message body. */ |
14 | | - messageFormatter?: MessageFormatter; |
| 5 | + /** The locale to use for the timestamp. */ |
| 6 | + locale: string; |
| 7 | + /** The timestamp of the message. */ |
| 8 | + timestamp: number; |
| 9 | + /** The message to display. */ |
| 10 | + message: string; |
| 11 | + /** The origin of the message. */ |
| 12 | + messageOrigin: 'local' | 'remote'; |
| 13 | + /** The sender's name. */ |
| 14 | + name?: string; |
| 15 | + /** Whether the message has been edited. */ |
| 16 | + hasBeenEdited?: boolean; |
15 | 17 | } |
16 | 18 |
|
17 | 19 | export const ChatEntry = ({ |
18 | | - entry, |
19 | | - hideName = false, |
20 | | - hideTimestamp = false, |
| 20 | + name, |
| 21 | + locale, |
| 22 | + timestamp, |
| 23 | + message, |
| 24 | + messageOrigin, |
| 25 | + hasBeenEdited = false, |
21 | 26 | className, |
22 | | - messageFormatter, |
23 | 27 | ...props |
24 | 28 | }: ChatEntryProps) => { |
25 | | - const time = new Date(entry.timestamp); |
26 | | - const hasBeenEdited = !!entry.editTimestamp; |
27 | | - const name = entry.from?.name || entry.from?.identity; |
28 | | - const locale = typeof navigator !== 'undefined' ? navigator.language : 'en-US'; |
29 | | - |
30 | | - const isUser = entry.from?.isLocal ?? false; |
31 | | - const messageOrigin = isUser ? 'remote' : 'local'; |
32 | | - const showHeader = !hideTimestamp || !hideName || hasBeenEdited; |
| 29 | + const time = new Date(timestamp); |
33 | 30 | const title = time.toLocaleTimeString(locale, { timeStyle: 'full' }); |
34 | 31 |
|
35 | | - const formattedMessage = useMemo(() => { |
36 | | - return messageFormatter ? messageFormatter(entry.message) : entry.message; |
37 | | - }, [entry.message, messageFormatter]); |
38 | | - |
39 | 32 | return ( |
40 | 33 | <li |
41 | 34 | title={title} |
42 | 35 | data-lk-message-origin={messageOrigin} |
43 | 36 | className={cn('group flex w-full flex-col gap-0.5', className)} |
44 | 37 | {...props} |
45 | 38 | > |
46 | | - {showHeader && ( |
| 39 | + {name && ( |
47 | 40 | <header className="text-muted-foreground flex text-sm"> |
48 | | - {!hideName && <strong className="mt-2">{name}</strong>} |
49 | | - {!hideTimestamp && ( |
50 | | - <span className="align-self-end ml-auto font-mono text-xs opacity-0 transition-opacity ease-linear group-hover:opacity-100"> |
51 | | - {hasBeenEdited && '*'} |
52 | | - {time.toLocaleTimeString(locale, { timeStyle: 'short' })} |
53 | | - </span> |
54 | | - )} |
| 41 | + {name && <strong className="mt-2">{name}</strong>} |
| 42 | + <span className="align-self-end ml-auto font-mono text-xs opacity-0 transition-opacity ease-linear group-hover:opacity-100"> |
| 43 | + {hasBeenEdited && '*'} |
| 44 | + {time.toLocaleTimeString(locale, { timeStyle: 'short' })} |
| 45 | + </span> |
55 | 46 | </header> |
56 | 47 | )} |
57 | 48 |
|
58 | | - <span className={cn('max-w-4/5 rounded-[20px]', isUser ? 'bg-muted ml-auto p-2' : 'mr-auto')}> |
59 | | - {formattedMessage} |
| 49 | + <span |
| 50 | + className={cn( |
| 51 | + 'max-w-4/5 rounded-[20px]', |
| 52 | + messageOrigin === 'local' ? 'bg-muted ml-auto p-2' : 'mr-auto' |
| 53 | + )} |
| 54 | + > |
| 55 | + {message} |
60 | 56 | </span> |
61 | 57 | </li> |
62 | 58 | ); |
|
0 commit comments