Skip to content

Commit 82e9bba

Browse files
v0.2.0
1 parent c44618f commit 82e9bba

File tree

16 files changed

+592
-151
lines changed

16 files changed

+592
-151
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quickblox-react-ui-kit",
3-
"version": "0.1.8",
3+
"version": "0.2.0",
44
"main": "dist/index-ui.js",
55
"license": "MIT",
66
"dependencies": {

src/App.tsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -194,18 +194,6 @@ function App() {
194194
prepareSDK(currentUser).catch();
195195
}, []);
196196

197-
// const { apiKey } = QBConfig.configAIApi.AIAnswerAssistWidgetConfig;
198-
// // eslint-disable-next-line @typescript-eslint/no-unsafe-call
199-
// const openAIConfiguration: Configuration = new Configuration({
200-
// apiKey,
201-
// });
202-
//
203-
// // eslint-disable-next-line @typescript-eslint/no-unsafe-call
204-
// const openAIApi: OpenAIApi = new OpenAIApi(openAIConfiguration);
205-
// const defaultIncomingMessageWidget = UseDefaultIncomingMessageWidget({
206-
// openAIApi,
207-
// });
208-
209197
// todo: uncomment authSecret
210198
return (
211199
<QuickBloxUIKitProvider
@@ -233,13 +221,6 @@ function App() {
233221
<Route
234222
path="/desktop-test-mock"
235223
element={
236-
// <QuickBloxUIKitDesktopLayout
237-
// theme={new DefaultTheme()}
238-
// IncomingMessageWidgetToRightPlaceHolder={
239-
// defaultIncomingMessageWidget
240-
// }
241-
// />
242-
243224
<QuickBloxUIKitDesktopLayout theme={new DefaultTheme()} />
244225
}
245226
/>

src/Presentation/Views/Base/BaseViewModel.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,15 @@ export type FunctionTypeDialogEntityToBoolean = (
6969
) => Promise<boolean>;
7070
export type FunctionTypeFileToFileEntity = (file: File) => Promise<FileEntity>;
7171
export type FunctionTypeJSXElement = () => JSX.Element;
72-
export type FunctionTypeChatMessagesToVoid = (
73-
lastMessage: string,
74-
messages: IChatMessage,
72+
// export type FunctionTypeChatMessagesToVoid = (
73+
// lastMessage: string,
74+
// messages: IChatMessage,
75+
// ) => void;
76+
export type FunctionTypeFileWithContextToToVoid = (
77+
file: File,
78+
context: IChatMessage[],
79+
) => void;
80+
export type FunctionTypeStringWithContextToVoid = (
81+
value: string,
82+
context: IChatMessage[],
7583
) => void;

src/Presentation/Views/Dialogs/Dialogs.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ const DialogsComponent: React.FC<DialogsProps> = ({
314314
<ColumnContainer>
315315
{useUpContent && upHeaderContent}
316316
{useHeader && HeaderContent}
317-
{/* {showSearchDialogs ? renderSearchDialogs() : null} */}
318317
{useSubContent && subHeaderContent}
319318
{dialogsViewModel?.loading && (
320319
// <div style={{ maxHeight: '44px', minHeight: '44px', height: '44px' }}>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {
2+
FunctionTypeFileWithContextToToVoid,
3+
FunctionTypeJSXElement,
4+
FunctionTypeStringWithContextToVoid,
5+
} from '../../../../../Views/Base/BaseViewModel';
6+
7+
export interface AIWidget {
8+
renderWidget: FunctionTypeJSXElement;
9+
textToWidget: FunctionTypeStringWithContextToVoid;
10+
fileToWidget: FunctionTypeFileWithContextToToVoid;
11+
textToContent: string | undefined;
12+
fileToContent: File | undefined;
13+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { useState, CSSProperties } from 'react';
2+
3+
type ErrorDescription = {
4+
title: string;
5+
action: () => void;
6+
};
7+
8+
type ErrorMessageIconProps = {
9+
errorMessageText: string;
10+
errorsDescriptions?: ErrorDescription[];
11+
};
12+
13+
const errorMessageIconStyles: { [key: string]: CSSProperties } = {
14+
errorIcon: {
15+
display: 'inline-block',
16+
position: 'relative',
17+
width: '21px', // Уменьшен размер круга
18+
height: '21px', // Уменьшен размер круга
19+
cursor: 'pointer',
20+
},
21+
circle: {
22+
width: '21px', // Уменьшен размер круга
23+
height: '21px', // Уменьшен размер круга
24+
backgroundColor: 'red',
25+
borderRadius: '50%',
26+
display: 'flex',
27+
justifyContent: 'center',
28+
alignItems: 'center',
29+
},
30+
exclamationMark: {
31+
color: 'white',
32+
fontSize: '12px', // Уменьшен размер восклицательного знака
33+
},
34+
errorMessage: {
35+
position: 'absolute',
36+
bottom: '100%', // Изменено выравнивание, чтобы текст был выше
37+
left: '50%',
38+
transform: 'translateX(-50%)',
39+
backgroundColor: 'lightgray',
40+
border: '1px solid gray',
41+
padding: '6px',
42+
borderRadius: '8px',
43+
whiteSpace: 'nowrap',
44+
display: 'inline-block',
45+
zIndex: 1, // Установлен zIndex, чтобы текст был выше других элементов
46+
},
47+
};
48+
49+
function ErrorMessageIcon({
50+
errorMessageText,
51+
errorsDescriptions,
52+
}: ErrorMessageIconProps) {
53+
const [isHovered, setIsHovered] = useState(false);
54+
55+
const handleMouseEnter = () => {
56+
setIsHovered(true);
57+
};
58+
59+
const handleMouseLeave = () => {
60+
setIsHovered(false);
61+
};
62+
63+
return (
64+
<div
65+
style={{
66+
...errorMessageIconStyles.errorIcon,
67+
...(isHovered ? { backgroundColor: 'yellow' } : {}),
68+
}}
69+
onMouseEnter={handleMouseEnter}
70+
onMouseLeave={handleMouseLeave}
71+
>
72+
<div style={errorMessageIconStyles.circle}>
73+
<span style={errorMessageIconStyles.exclamationMark}>!</span>
74+
</div>
75+
{isHovered && (
76+
<div style={errorMessageIconStyles.errorMessage}>
77+
{errorMessageText}
78+
{errorsDescriptions?.map((item, index) => (
79+
<div
80+
key={index}
81+
style={{
82+
padding: '4px',
83+
cursor: 'pointer',
84+
}}
85+
onClick={() => {
86+
item.action();
87+
}}
88+
>
89+
{item.title}
90+
</div>
91+
))}
92+
</div>
93+
)}
94+
</div>
95+
);
96+
}
97+
98+
export default ErrorMessageIcon;
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { useState } from 'react';
2+
import { AIWidget } from './AIWidget';
3+
import AIWidgetIcon from '../../../svgs/Icons/Media/AIWidget';
4+
import { IChatMessage } from '../../../../../Views/Base/BaseViewModel';
5+
import { stringifyError } from '../../../../../../utils/parse';
6+
import ErrorMessageIcon from './ErrorMessageIcon';
7+
8+
interface MessageWidgetProps {
9+
// https://api.openai.com/v1/chat/completions'
10+
// api: 'v1/chat/completions',
11+
// servername: 'https://myproxy.com',
12+
// https://func270519800.azurewebsites.net/api/TranslateTextToEng
13+
servername: string;
14+
api: string;
15+
port: string;
16+
sessionToken: string;
17+
}
18+
export default function UseDefaultAIAssistAnswerWidgetWithProxy({
19+
servername,
20+
api,
21+
port,
22+
sessionToken,
23+
}: MessageWidgetProps): AIWidget {
24+
const [errorMessage, setErrorMessage] = useState<string>('');
25+
26+
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
27+
const fileToWidget = (file: File, context: IChatMessage[]): void => {};
28+
29+
const renderWidget = (): JSX.Element => {
30+
if (errorMessage && errorMessage.length > 0) {
31+
const errorsDescriptions:
32+
| { title: string; action: () => void }[]
33+
| undefined = [];
34+
35+
return (
36+
<ErrorMessageIcon
37+
errorMessageText={errorMessage}
38+
errorsDescriptions={errorsDescriptions}
39+
/>
40+
);
41+
}
42+
43+
return <AIWidgetIcon applyZoom color="green" />;
44+
};
45+
46+
// async function getData(
47+
// textToSend: string,
48+
// dialogMessages: ChatCompletionRequestMessage[],
49+
// ): Promise<string> {
50+
// //
51+
// const apiEndpoint = 'https://api.openai.com/v1/chat/completions';
52+
// const { apiKey } = QBConfig.configAIApi.AIAnswerAssistWidgetConfig; // Замените на ваш реальный ключ API
53+
// const model = 'gpt-3.5-turbo';
54+
// const requestOptions = {
55+
// method: 'POST',
56+
// headers: {
57+
// 'Content-Type': 'application/json',
58+
// Authorization: `Bearer ${apiKey}`,
59+
// },
60+
// body: JSON.stringify({
61+
// messages: [...dialogMessages, { role: 'user', content: textToSend }],
62+
// model,
63+
// temperature: 0.5,
64+
// }),
65+
// };
66+
//
67+
async function getData(
68+
textToSend: string,
69+
dialogMessages: IChatMessage[],
70+
): Promise<string> {
71+
let outputMessage = '';
72+
const apiEndpoint = `${servername}${port}${api}`;
73+
const apiKey = sessionToken; // Замените на ваш реальный ключ API
74+
const model = 'gpt-3.5-turbo';
75+
const prompt = `Respond as a knowledgeable customer support specialist with access to ChatGPT features, and provide a simple and informative response to the inquiry :"${textToSend}"`;
76+
77+
const requestOptions = {
78+
method: 'POST',
79+
headers: {
80+
'Content-Type': 'application/json',
81+
Authorization: `Bearer ${apiKey}`,
82+
},
83+
body: JSON.stringify({
84+
messages: [...dialogMessages, { role: 'user', content: prompt }],
85+
model,
86+
temperature: 0.5,
87+
}),
88+
};
89+
90+
//
91+
try {
92+
const response = await fetch(apiEndpoint, requestOptions);
93+
const data = await response.json();
94+
95+
outputMessage = data.choices[0].message?.content || '';
96+
} catch (err) {
97+
outputMessage = stringifyError(err);
98+
setErrorMessage(outputMessage);
99+
}
100+
101+
return outputMessage;
102+
}
103+
104+
const [textFromWidgetToContent, setTextFromWidgetToContent] = useState('');
105+
// const textToWidget = (value: string, context: IChatMessage[]): void => {
106+
// if (value && value.length > 0) {
107+
// // eslint-disable-next-line promise/catch-or-return
108+
// getOpenAIApiData(value, context as ChatCompletionRequestMessage[]).then(
109+
// // eslint-disable-next-line promise/always-return
110+
// (data) => {
111+
// setTextFromWidgetToContent(data);
112+
// },
113+
// );
114+
// }
115+
// };
116+
117+
const textToWidget = (value: string, context: IChatMessage[]): void => {
118+
if (value && value.length > 0) {
119+
// eslint-disable-next-line promise/catch-or-return
120+
getData(value, context).then(
121+
// eslint-disable-next-line promise/always-return
122+
(data) => {
123+
setTextFromWidgetToContent(data);
124+
},
125+
);
126+
}
127+
};
128+
129+
return {
130+
fileToContent: undefined,
131+
textToContent: textFromWidgetToContent,
132+
fileToWidget,
133+
renderWidget,
134+
textToWidget,
135+
};
136+
}

src/Presentation/components/UI/Dialogs/MessagesView/InputWidget/UseDefaultTextInputWidget.tsx renamed to src/Presentation/components/UI/Dialogs/MessagesView/AIWidgets/UseDefaultTextInputWidget.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from 'react';
2-
import { InputWidget } from './InputWidget';
2+
import { AIWidget } from './AIWidget';
33

4-
export default function useDefaultTextInputWidget(): InputWidget {
4+
export default function useDefaultTextInputWidget(): AIWidget {
55
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
66
const fileToWidget = (file: File): void => {};
77

@@ -51,8 +51,8 @@ export default function useDefaultTextInputWidget(): InputWidget {
5151
};
5252

5353
return {
54-
fileToInput: undefined,
55-
textToInput: textFromWidgetToInput,
54+
fileToContent: undefined,
55+
textToContent: textFromWidgetToInput,
5656
fileToWidget,
5757
renderWidget,
5858
textToWidget,

src/Presentation/components/UI/Dialogs/MessagesView/InputWidget/useDefaultVoiceInputWidget.tsx renamed to src/Presentation/components/UI/Dialogs/MessagesView/AIWidgets/useDefaultVoiceInputWidget.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useState } from 'react';
2-
import { InputWidget } from './InputWidget';
2+
import { AIWidget } from './AIWidget';
33
// eslint-disable-next-line @typescript-eslint/no-unused-vars
44
import VoiceIcon from '../../../svgs/Icons/Actions/Voice';
55

6-
export default function useDefaultVoiceInputWidget(): InputWidget {
6+
export default function useDefaultVoiceInputWidget(): AIWidget {
77
const renderWidget = (): JSX.Element => {
88
// return <VoiceIcon width="21" height="18" applyZoom color="red" />;
99
return (
@@ -77,8 +77,8 @@ export default function useDefaultVoiceInputWidget(): InputWidget {
7777
};
7878

7979
return {
80-
fileToInput: audioFromWidgetToInput,
81-
textToInput: textFromWidgetToInput,
80+
fileToContent: audioFromWidgetToInput,
81+
textToContent: textFromWidgetToInput,
8282
fileToWidget,
8383
renderWidget,
8484
textToWidget,

0 commit comments

Comments
 (0)