Skip to content

Commit 4d9d776

Browse files
committed
feat: add AITypingIndicatorView as well and include to Vite app
1 parent 26de263 commit 4d9d776

File tree

6 files changed

+103
-0
lines changed

6 files changed

+103
-0
lines changed

examples/vite/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat';
22
import {
3+
AITypingIndicatorView,
34
Channel,
45
ChannelAvatar,
56
ChannelHeader,
@@ -88,6 +89,7 @@ const App = () => {
8889
<Window>
8990
<ChannelHeader Avatar={ChannelAvatar} />
9091
<MessageList returnAllReadData />
92+
<AITypingIndicatorView />
9193
<MessageInput focus />
9294
</Window>
9395
<Thread virtualized />
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
3+
import { Channel } from 'stream-chat';
4+
5+
import { AIStates, useAIState } from './hooks/useAIState';
6+
7+
import { useChannelStateContext, useTranslationContext } from '../../context';
8+
import type { DefaultStreamChatGenerics } from '../../types/types';
9+
10+
export type AITypingIndicatorViewProps<
11+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
12+
> = {
13+
channel?: Channel<StreamChatGenerics>;
14+
};
15+
16+
export const AITypingIndicatorView = <
17+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
18+
>({
19+
channel: channelFromProps,
20+
}: AITypingIndicatorViewProps<StreamChatGenerics>) => {
21+
const { t } = useTranslationContext();
22+
const { channel: channelFromContext } = useChannelStateContext<StreamChatGenerics>(
23+
'AITypingIndicatorView',
24+
);
25+
const channel = channelFromProps || channelFromContext;
26+
const { aiState } = useAIState(channel);
27+
const allowedStates = {
28+
[AIStates.Thinking]: t('Thinking...'),
29+
[AIStates.Generating]: t('Generating...'),
30+
};
31+
32+
return aiState in allowedStates ? (
33+
<div style={{ backgroundColor: 'gainsboro', padding: '0px 8px' }}>
34+
<p style={{ color: 'black' }}>{allowedStates[aiState]}</p>
35+
</div>
36+
) : null;
37+
};
38+
39+
AITypingIndicatorView.displayName = 'AITypingIndicatorView{messageSimple{content}}';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { Channel, Event } from 'stream-chat';
4+
5+
import type { DefaultStreamChatGenerics } from '../../../types/types';
6+
7+
export type AIStateType =
8+
| 'AI_STATE_ERROR'
9+
| 'AI_STATE_EXTERNAL_SOURCES'
10+
| 'AI_STATE_GENERATING'
11+
| 'AI_STATE_IDLE'
12+
| 'AI_STATE_THINKING'
13+
| string;
14+
15+
export const AIStates = {
16+
Error: 'AI_STATE_ERROR',
17+
ExternalSources: 'AI_STATE_EXTERNAL_SOURCES',
18+
Generating: 'AI_STATE_GENERATING',
19+
Idle: 'AI_STATE_IDLE',
20+
Thinking: 'AI_STATE_THINKING',
21+
};
22+
23+
export const useAIState = <
24+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
25+
>(
26+
channel: Channel<StreamChatGenerics>,
27+
) => {
28+
const [aiState, setAiState] = useState<AIStateType>(AIStates.Idle);
29+
30+
useEffect(() => {
31+
const indicatorChangedListener = channel.on(
32+
// @ts-ignore
33+
'ai_indicator_changed',
34+
(event: Event<StreamChatGenerics>) => {
35+
const { cid } = event;
36+
const state = event.state as AIStateType;
37+
if (channel.cid === cid) {
38+
setAiState(state);
39+
}
40+
},
41+
);
42+
43+
// @ts-ignore
44+
const indicatorClearedListener = channel.on('ai_indicator_clear', (event) => {
45+
const { cid } = event;
46+
if (channel.cid === cid) {
47+
setAiState(AIStates.Idle);
48+
}
49+
});
50+
51+
return () => {
52+
indicatorChangedListener.unsubscribe();
53+
indicatorClearedListener.unsubscribe();
54+
};
55+
}, [channel]);
56+
57+
return { aiState };
58+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './AITypingIndicatorView';
2+
export * from './hooks/useAIState';

src/components/Message/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export * from './QuotedMessage';
1313
export * from './renderText';
1414
export * from './types';
1515
export * from './utils';
16+
export * from './StreamingMessageView';
1617
export type { TimestampProps } from './Timestamp';

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export * from './UserItem';
3636
export * from './Window';
3737
export * from './Threads';
3838
export * from './ChatView';
39+
export * from './AITypingIndicatorView';
3940

4041
export { UploadButton } from './ReactFileUtilities';
4142
export type { UploadButtonProps } from './ReactFileUtilities';

0 commit comments

Comments
 (0)