@tencent-rtc/mcp 1.7.2 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +25 -17
- package/knowledge/bm25.js +20 -11
- package/knowledge/path-meta.js +23 -1
- package/knowledge/search/generic-rerank.js +123 -0
- package/knowledge/search/search-pass.js +8 -1
- package/package.json +1 -1
- package/resource/index/manifest.json +1 -1
- package/resource/knowledge/callkit/best-practice/android/video-chat-dating.md +497 -0
- package/resource/knowledge/callkit/best-practice/flutter/android-keep-call-alive-background.md +79 -0
- package/resource/knowledge/callkit/best-practice/flutter/background-incoming-call-no-notification.md +99 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-keep-call-alive-background.md +93 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-startup-crash-version-voip.md +68 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-engine.md +386 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-event.md +315 -0
- package/resource/knowledge/callkit/faq/uniapp-miniprogram.md +204 -0
- package/resource/knowledge/callkit/preset/harmonyos/api.md +362 -0
- package/resource/knowledge/callkit/preset/harmonyos/integration.md +318 -0
- package/resource/knowledge/callkit/preset/miniprogram/api-uniapp.md +430 -0
- package/resource/knowledge/callkit/preset/miniprogram/api.md +293 -0
- package/resource/knowledge/callkit/preset/miniprogram/integration.md +294 -0
- package/resource/knowledge/callkit/preset/miniprogram/uniapp-integration.md +224 -0
- package/resource/knowledge/callkit/preset/uni-app/api.md +339 -0
- package/resource/knowledge/callkit/preset/uni-app/integration.md +243 -0
- package/resource/knowledge/chat/best-practice/common/web-sdk-log-level.md +76 -0
- package/resource/knowledge/chat/best-practice/miniprogram/desktop-wechat-miniprogram-error.md +79 -0
- package/resource/knowledge/chat/preset/react/h5/integration.md +964 -0
- package/resource/knowledge/chat/preset/vue/h5/integration.md +717 -0
- package/resource/knowledge/roomkit/best-practice/vue/screen-share-auto-stop-on-leave.md +62 -0
- package/resource/knowledge/roomkit/core-sdk/react/network-proxy.md +83 -0
- package/resource/knowledge/roomkit/core-sdk/vue/network-proxy.md +83 -0
- package/resource/knowledge/roomkit/core-sdk/vue/recording.md +245 -0
- package/resource/knowledge/roomkit/core-sdk/vue/whiteboard-annotation.md +196 -0
- package/resource/knowledge/roomkit/faq/error-code.md +103 -0
- package/resource/knowledge/roomkit/preset/harmonyos/integration.md +461 -0
- package/resource/knowledge/roomkit/preset/react/features/network-proxy.md +79 -0
- package/resource/knowledge/roomkit/preset/uniapp-app/integration.md +580 -0
- package/resource/knowledge/roomkit/preset/vue/features/ai-caption-translation.md +69 -0
- package/resource/knowledge/roomkit/preset/vue/features/ai-denoiser.md +67 -0
- package/resource/knowledge/roomkit/preset/vue/features/network-proxy.md +79 -0
- package/resource/knowledge/roomkit/preset/vue/features/recording.md +85 -0
- package/resource/knowledge/roomkit/preset/vue/features/whiteboard-annotation.md +86 -0
- package/resource/knowledge/rtcengine/web/plugin/23-advanced-support-detection.md +6 -6
- package/resource/knowledge/rtcengine/web/plugin/31-advanced-plugins-overview.md +8 -6
- package/resource/knowledge/rtcengine/web/plugin/42-advanced-realtime-transcriber.md +30 -10
- package/server.js +1 -1
- package/utils/preflight-route.js +18 -6
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
TUIKit is a React component library built on top of the Tencent Cloud Chat SDK. It provides common UI components covering conversations, chat, groups, and more. This guide walks you through quickly integrating TUIKit and implementing core features.
|
|
2
|
+
|
|
3
|
+
>
|
|
4
|
+
|
|
5
|
+
>
|
|
6
|
+
|
|
7
|
+
## Key Concepts
|
|
8
|
+
|
|
9
|
+
chat-uikit-react consists of core UI components including ConversationListH5, MessageListH5, ChatHeaderH5, MessageInputH5, ChatSettingH5, SearchH5, and ContactH5. Each component is responsible for rendering different content.
|
|
10
|
+
1. ConversationListH5 provides the conversation list component.
|
|
11
|
+
|
|
12
|
+
2. Chat provides the conversation container component.
|
|
13
|
+
|
|
14
|
+
3. MessageListH5 provides the message list component for conversations.
|
|
15
|
+
|
|
16
|
+
4. ChatHeaderH5 provides the conversation header component.
|
|
17
|
+
|
|
18
|
+
5. MessageInputH5 provides the message input component.
|
|
19
|
+
|
|
20
|
+
6. ChatSettingH5 provides the management component for one-to-one and group chats.
|
|
21
|
+
|
|
22
|
+
7. SearchH5 provides the cloud search component.
|
|
23
|
+
|
|
24
|
+
8. ContactH5 provides the contacts component.
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
- Node.js v18 or later, LTS version recommended
|
|
28
|
+
|
|
29
|
+
- React^18.2 || React^19.0.0
|
|
30
|
+
|
|
31
|
+
- TypeScript@^5.0.0
|
|
32
|
+
|
|
33
|
+
## Quick Start with the Complete Example Project
|
|
34
|
+
|
|
35
|
+
If you want to quickly see the full result, or need a code reference during integration, you can clone our ready-made example project. The example project has completed Chat UIKit dependency installation, component imports, initialization configuration, and basic page integration — ideal as an integration reference.
|
|
36
|
+
``` bash
|
|
37
|
+
git clone https://gitee.com/tencent-cloud-uikit/rtc-chat-web.git
|
|
38
|
+
cd chat-mobile-react
|
|
39
|
+
npm install
|
|
40
|
+
npm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> **Note:**
|
|
44
|
+
> Example project: [View on Gitee](https://gitee.com/tencent-cloud-uikit/rtc-chat-web)。
|
|
45
|
+
>
|
|
46
|
+
> The example project helps you quickly experience the integration flow. If you need to integrate into an existing project, continue reading the steps below.
|
|
47
|
+
>
|
|
48
|
+
|
|
49
|
+
## Create a Project
|
|
50
|
+
|
|
51
|
+
Use Vite to create a new React project named chat-integration-react-h5 and follow the scaffold prompts to initialize it. After the initial project starts, remove the scaffold's sample files (default styles in src/App.css, src/index.css, src/assets, icons in public, etc.), keeping only src/main.tsx and index.html.
|
|
52
|
+
|
|
53
|
+
> **Warning:**
|
|
54
|
+
> 1. It is recommended to use npm for installation, as npm will automatically download required peerDependencies.
|
|
55
|
+
> 2. `npm create vite@latest` uses the latest Vite version, which may conflict with older Node.js versions. Please check your environment configuration.
|
|
56
|
+
|
|
57
|
+
[shell]
|
|
58
|
+
``` bash
|
|
59
|
+
npm create vite@latest
|
|
60
|
+
|
|
61
|
+
◇ Project name:
|
|
62
|
+
│ chat-integration-react-h5
|
|
63
|
+
│
|
|
64
|
+
◇ Select a framework:
|
|
65
|
+
│ React
|
|
66
|
+
│
|
|
67
|
+
◇ Select a variant:
|
|
68
|
+
│ TypeScript + React Compiler
|
|
69
|
+
│
|
|
70
|
+
◇ Use ESLint instead of Oxlint?
|
|
71
|
+
│ Yes (ESLint)
|
|
72
|
+
│
|
|
73
|
+
◇ Install with npm and start now?
|
|
74
|
+
│ Yes
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Download and Import Components
|
|
78
|
+
|
|
79
|
+
### Step 1: Install Dependencies
|
|
80
|
+
|
|
81
|
+
Download [chat-uikit-react](https://www.npmjs.com/package/@tencentcloud/chat-uikit-react) via npm and use it in your project.
|
|
82
|
+
|
|
83
|
+
[shell]
|
|
84
|
+
``` bash
|
|
85
|
+
npm install @tencentcloud/chat-uikit-react @tencentcloud/uikit-base-component-react
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Step 2: Import Components
|
|
89
|
+
1. Configure the mobile viewport.
|
|
90
|
+
|
|
91
|
+
The first step for a great mobile experience: in the project root, set the default viewport in index.html to full-screen coverage with locked scaling, making the page feel more like a native app.
|
|
92
|
+
|
|
93
|
+
``` java
|
|
94
|
+
<!DOCTYPE html>
|
|
95
|
+
<html lang="zh-CN">
|
|
96
|
+
<head>
|
|
97
|
+
<meta charset="UTF-8" />
|
|
98
|
+
<!-- H5 viewport: 覆盖刘海屏区域,锁定缩放,营造类原生应用的体验 -->
|
|
99
|
+
<meta
|
|
100
|
+
name="viewport"
|
|
101
|
+
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
|
102
|
+
/>
|
|
103
|
+
<title>Chat UIKit H5 Quickstart (React)</title>
|
|
104
|
+
</head>
|
|
105
|
+
<body>
|
|
106
|
+
<div id="root"></div>
|
|
107
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
110
|
+
```
|
|
111
|
+
2. Configure global base styles.
|
|
112
|
+
|
|
113
|
+
Create **src/styles.css** and set the global base styles.
|
|
114
|
+
|
|
115
|
+
``` css
|
|
116
|
+
/* ------- 移动端基础布局 ------- */
|
|
117
|
+
* {
|
|
118
|
+
box-sizing: border-box;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
html,
|
|
122
|
+
body,
|
|
123
|
+
#root {
|
|
124
|
+
width: 100%;
|
|
125
|
+
height: 100%;
|
|
126
|
+
margin: 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
body {
|
|
130
|
+
/* 使用 dvh 来处理 iOS Safari 地址栏. */
|
|
131
|
+
min-height: 100vh;
|
|
132
|
+
min-height: 100dvh;
|
|
133
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
134
|
+
color: #1f2329;
|
|
135
|
+
background: #f5f6f8;
|
|
136
|
+
/* 移除移动版 WebKit 上的点击亮块。 */
|
|
137
|
+
-webkit-tap-highlight-color: transparent;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
button {
|
|
141
|
+
font: inherit;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* 16px 可防止 iOS 在输入框获得焦点时自动放大屏幕。 */
|
|
146
|
+
input {
|
|
147
|
+
font-size: 16px;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
3. Configure the app entry point.
|
|
151
|
+
|
|
152
|
+
Write the following into **src/main.tsx** to import styles at the app entry.
|
|
153
|
+
|
|
154
|
+
``` typescript
|
|
155
|
+
import { createRoot } from 'react-dom/client';
|
|
156
|
+
import App from './App';
|
|
157
|
+
import './styles.css';
|
|
158
|
+
|
|
159
|
+
createRoot(document.getElementById('root')!).render(
|
|
160
|
+
<App />,
|
|
161
|
+
);
|
|
162
|
+
```
|
|
163
|
+
4. Configure the home page.
|
|
164
|
+
|
|
165
|
+
Write the following into **src/App.tsx** to route between the login page and chat page.
|
|
166
|
+
|
|
167
|
+
``` typescript
|
|
168
|
+
import { useState } from 'react';
|
|
169
|
+
import { LoginStatus, UIKitProvider, useLoginState } from '@tencentcloud/chat-uikit-react';
|
|
170
|
+
import { LoginView } from './LoginView';
|
|
171
|
+
import { ChatLayout } from './ChatLayout';
|
|
172
|
+
import { languageResources } from './i18n';
|
|
173
|
+
|
|
174
|
+
function App() {
|
|
175
|
+
const { status } = useLoginState();
|
|
176
|
+
const isLogined = status === LoginStatus.SUCCESS;
|
|
177
|
+
|
|
178
|
+
// 语言在此处进行维护,并通过 UIKitProvider 的 `language` 属性进行配置。
|
|
179
|
+
// 这是控制语言的推荐方式
|
|
180
|
+
// 更新这个单一数据源,它驱动整个 UIKit。
|
|
181
|
+
const [language, setLanguage] = useState('zh-CN');
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<UIKitProvider language={language} languageResources={languageResources}>
|
|
185
|
+
{isLogined
|
|
186
|
+
? <ChatLayout onChangeLanguage={setLanguage} />
|
|
187
|
+
: <LoginView language={language} onChangeLanguage={setLanguage} />}
|
|
188
|
+
</UIKitProvider>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default App;
|
|
193
|
+
```
|
|
194
|
+
5. Configure the login page and chat main page.
|
|
195
|
+
|
|
196
|
+
Create **src/LoginView.tsx** and **src/LoginView.css** and write the following code to build the login page.
|
|
197
|
+
|
|
198
|
+
[src/LoginView.tsx]
|
|
199
|
+
``` typescript
|
|
200
|
+
import { useState, type SubmitEvent } from 'react';
|
|
201
|
+
import { useLoginState, useUIKit } from '@tencentcloud/chat-uikit-react';
|
|
202
|
+
import './LoginView.css';
|
|
203
|
+
|
|
204
|
+
// 语言切换器中显示的语言。标签使用本来的语言,确保用户
|
|
205
|
+
// 不受当前 UI 语言影响,始终能识别自己的选项。
|
|
206
|
+
const LANGUAGES = [
|
|
207
|
+
{ value: 'zh-CN', label: '中文' },
|
|
208
|
+
{ value: 'en-US', label: 'English' },
|
|
209
|
+
];
|
|
210
|
+
|
|
211
|
+
interface LoginViewProps {
|
|
212
|
+
language: string;
|
|
213
|
+
onChangeLanguage: (language: string) => void;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function LoginView({ language, onChangeLanguage }: LoginViewProps) {
|
|
217
|
+
const { login } = useLoginState();
|
|
218
|
+
const { t } = useUIKit();
|
|
219
|
+
const [sdkAppId, setSdkAppId] = useState<number>();
|
|
220
|
+
const [userId, setUserId] = useState('');
|
|
221
|
+
const [userSig, setUserSig] = useState('');
|
|
222
|
+
const [loading, setLoading] = useState(false);
|
|
223
|
+
const [error, setError] = useState('');
|
|
224
|
+
|
|
225
|
+
const handleSubmit = async (e: SubmitEvent) => {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
setError('');
|
|
228
|
+
if (!sdkAppId || !userId || !userSig) {
|
|
229
|
+
setError(t('demo.error.required'));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
setLoading(true);
|
|
234
|
+
try {
|
|
235
|
+
await login({ SDKAppID: Number(sdkAppId), userID: userId, userSig });
|
|
236
|
+
// On success the login gate in App.tsx switches to ChatLayout automatically.
|
|
237
|
+
} catch (err) {
|
|
238
|
+
setError(err instanceof Error ? err.message : t('demo.error.loginFailed'));
|
|
239
|
+
} finally {
|
|
240
|
+
setLoading(false);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<div className="login">
|
|
246
|
+
<div className="login__lang" role="group" aria-label="Language">
|
|
247
|
+
{LANGUAGES.map(item => (
|
|
248
|
+
<button
|
|
249
|
+
key={item.value}
|
|
250
|
+
type="button"
|
|
251
|
+
className={item.value === language ? 'login__lang-btn login__lang-btn--active' : 'login__lang-btn'}
|
|
252
|
+
onClick={() => onChangeLanguage(item.value)}
|
|
253
|
+
>
|
|
254
|
+
{item.label}
|
|
255
|
+
</button>
|
|
256
|
+
))}
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<form className="login__card" onSubmit={handleSubmit}>
|
|
260
|
+
<h1 className="login__title">{t('demo.appTitle')}</h1>
|
|
261
|
+
|
|
262
|
+
<label className="login__field">
|
|
263
|
+
<span>{t('demo.field.sdkAppId')}</span>
|
|
264
|
+
<input
|
|
265
|
+
type="number"
|
|
266
|
+
placeholder={t('demo.placeholder.sdkAppId')}
|
|
267
|
+
value={sdkAppId || ''}
|
|
268
|
+
onChange={e => setSdkAppId(Number(e.target.value))}
|
|
269
|
+
/>
|
|
270
|
+
</label>
|
|
271
|
+
|
|
272
|
+
<label className="login__field">
|
|
273
|
+
<span>{t('demo.field.userId')}</span>
|
|
274
|
+
<input
|
|
275
|
+
type="text"
|
|
276
|
+
placeholder={t('demo.placeholder.userId')}
|
|
277
|
+
value={userId}
|
|
278
|
+
onChange={e => setUserId(e.target.value)}
|
|
279
|
+
/>
|
|
280
|
+
</label>
|
|
281
|
+
|
|
282
|
+
<label className="login__field">
|
|
283
|
+
<span>{t('demo.field.userSig')}</span>
|
|
284
|
+
<input
|
|
285
|
+
type="text"
|
|
286
|
+
placeholder={t('demo.placeholder.userSig')}
|
|
287
|
+
value={userSig}
|
|
288
|
+
onChange={e => setUserSig(e.target.value)}
|
|
289
|
+
/>
|
|
290
|
+
</label>
|
|
291
|
+
|
|
292
|
+
{error && <p className="login__error">{error}</p>}
|
|
293
|
+
|
|
294
|
+
<button className="login__submit" type="submit" disabled={loading}>
|
|
295
|
+
{loading ? t('demo.loggingIn') : t('demo.login')}
|
|
296
|
+
</button>
|
|
297
|
+
</form>
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
[src/LoginView.css]
|
|
304
|
+
``` css
|
|
305
|
+
.login {
|
|
306
|
+
position: relative;
|
|
307
|
+
display: flex;
|
|
308
|
+
width: 100%;
|
|
309
|
+
height: 100dvh;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
padding: max(20px, env(safe-area-inset-top)) 20px max(20px, env(safe-area-inset-bottom));
|
|
313
|
+
/* Soft natural-white backdrop with a subtle top-down gradient for depth. */
|
|
314
|
+
background: linear-gradient(180deg, #ffffff 0%, #f3f5f8 100%);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Segmented language switcher, pinned to the top-right. */
|
|
318
|
+
.login__lang {
|
|
319
|
+
position: absolute;
|
|
320
|
+
top: calc(16px + env(safe-area-inset-top));
|
|
321
|
+
right: 16px;
|
|
322
|
+
display: inline-flex;
|
|
323
|
+
padding: 3px;
|
|
324
|
+
border-radius: 999px;
|
|
325
|
+
background: #eef0f3;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.login__lang-btn {
|
|
329
|
+
min-width: 56px;
|
|
330
|
+
padding: 6px 12px;
|
|
331
|
+
border: 0;
|
|
332
|
+
border-radius: 999px;
|
|
333
|
+
background: transparent;
|
|
334
|
+
color: #6b7280;
|
|
335
|
+
font-size: 13px;
|
|
336
|
+
font-weight: 500;
|
|
337
|
+
transition: background 0.2s, color 0.2s;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.login__lang-btn--active {
|
|
341
|
+
background: #fff;
|
|
342
|
+
color: #1f2329;
|
|
343
|
+
box-shadow: 0 1px 2px rgb(16 24 40 / 10%);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.login__card {
|
|
347
|
+
display: flex;
|
|
348
|
+
width: 100%;
|
|
349
|
+
max-width: 360px;
|
|
350
|
+
flex-direction: column;
|
|
351
|
+
gap: 16px;
|
|
352
|
+
padding: 28px 24px;
|
|
353
|
+
border-radius: 16px;
|
|
354
|
+
background: #fff;
|
|
355
|
+
/* Layered, low-opacity shadow for a soft, natural lift off the white bg. */
|
|
356
|
+
border: 1px solid rgb(16 24 40 / 4%);
|
|
357
|
+
box-shadow:
|
|
358
|
+
0 1px 2px rgb(16 24 40 / 4%),
|
|
359
|
+
0 12px 32px rgb(16 24 40 / 8%);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.login__title {
|
|
363
|
+
margin: 0 0 4px;
|
|
364
|
+
font-size: 20px;
|
|
365
|
+
font-weight: 600;
|
|
366
|
+
text-align: center;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.login__field {
|
|
370
|
+
display: flex;
|
|
371
|
+
flex-direction: column;
|
|
372
|
+
gap: 6px;
|
|
373
|
+
font-size: 13px;
|
|
374
|
+
color: #6b7280;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.login__field input {
|
|
378
|
+
padding: 12px 14px;
|
|
379
|
+
border: 1px solid #e8eaed;
|
|
380
|
+
border-radius: 10px;
|
|
381
|
+
outline: none;
|
|
382
|
+
appearance: none;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.login__field input:focus {
|
|
386
|
+
border-color: #1464ff;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.login__error {
|
|
390
|
+
margin: 0;
|
|
391
|
+
color: #e34d59;
|
|
392
|
+
font-size: 13px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.login__submit {
|
|
396
|
+
padding: 13px;
|
|
397
|
+
border: 0;
|
|
398
|
+
border-radius: 10px;
|
|
399
|
+
background: #1464ff;
|
|
400
|
+
color: #fff;
|
|
401
|
+
font-size: 16px;
|
|
402
|
+
font-weight: 600;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.login__submit:disabled {
|
|
406
|
+
opacity: 0.6;
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Create **src/ChatLayout.tsx** and **src/ChatLayout.css** and write the following code:
|
|
411
|
+
|
|
412
|
+
[src/ChatLayout.tsx]
|
|
413
|
+
``` typescript
|
|
414
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
415
|
+
import {
|
|
416
|
+
Chat,
|
|
417
|
+
ChatHeaderH5,
|
|
418
|
+
ChatSettingH5,
|
|
419
|
+
ContactInfoH5,
|
|
420
|
+
ContactListH5,
|
|
421
|
+
ConversationListH5,
|
|
422
|
+
MessageInputH5,
|
|
423
|
+
MessageListH5,
|
|
424
|
+
SearchH5,
|
|
425
|
+
VariantType,
|
|
426
|
+
useChatContext,
|
|
427
|
+
useLoginState,
|
|
428
|
+
useUIKit,
|
|
429
|
+
} from '@tencentcloud/chat-uikit-react';
|
|
430
|
+
import { IconBulletpoint, IconSearch } from '@tencentcloud/uikit-base-component-react';
|
|
431
|
+
import './ChatLayout.css';
|
|
432
|
+
|
|
433
|
+
type MobileView = 'conversations' | 'chat' | 'contacts' | 'contactInfo' | 'search' | 'setting';
|
|
434
|
+
|
|
435
|
+
interface ChatLayoutProps {
|
|
436
|
+
onChangeLanguage: (language: string) => void;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** // 全屏视图;仅当前激活的视图可见(保持挂载以保留状态)。 */
|
|
440
|
+
function Screen({ active, children }: { active: boolean; children: ReactNode }) {
|
|
441
|
+
return (
|
|
442
|
+
<section className="screen" style={{ zIndex: active ? 1 : 0, visibility: active ? 'visible' : 'hidden' }}>
|
|
443
|
+
{children}
|
|
444
|
+
</section>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function ChatLayout({ onChangeLanguage }: ChatLayoutProps) {
|
|
449
|
+
|
|
450
|
+
const { activeConversationID, activeConversation, sendMessage, setActiveConversation } = useChatContext();
|
|
451
|
+
const { loginUserInfo, logout } = useLoginState();
|
|
452
|
+
const { t, language } = useUIKit();
|
|
453
|
+
const [activeView, setActiveView] = useState<MobileView>('conversations');
|
|
454
|
+
// 记住搜索是从哪里打开的,使其返回按钮能回到原处。
|
|
455
|
+
const [previousView, setPreviousView] = useState<MobileView>('chat');
|
|
456
|
+
const defaultOpenConversationID = 'C2Cadministrator';
|
|
457
|
+
|
|
458
|
+
const activeViewRef = useRef(activeView);
|
|
459
|
+
const prevConversationIDRef = useRef(activeConversationID);
|
|
460
|
+
// 仅发送一次欢迎语
|
|
461
|
+
const sendWelcomeMessageOnce = useRef(false);
|
|
462
|
+
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
activeViewRef.current = activeView;
|
|
465
|
+
}, [activeView]);
|
|
466
|
+
|
|
467
|
+
useEffect(() => {
|
|
468
|
+
const changed = prevConversationIDRef.current !== activeConversationID;
|
|
469
|
+
prevConversationIDRef.current = activeConversationID;
|
|
470
|
+
if (changed && activeConversationID && activeViewRef.current === 'conversations') {
|
|
471
|
+
setActiveView('chat');
|
|
472
|
+
}
|
|
473
|
+
}, [activeConversationID]);
|
|
474
|
+
|
|
475
|
+
// 应用创建(组件挂载)时仅发送一次欢迎语;切换会话不会重复发送。
|
|
476
|
+
useEffect(() => {
|
|
477
|
+
const timer = setTimeout(() => {
|
|
478
|
+
if (activeConversation?.conversationID === defaultOpenConversationID && !sendWelcomeMessageOnce.current) {
|
|
479
|
+
sendMessage({
|
|
480
|
+
type: 'textMessage',
|
|
481
|
+
text: `欢迎体验 Chat React 移动端WEB应用 Demo~
|
|
482
|
+
|
|
483
|
+
您可以按照以下顺序体验 IM 的核心功能:
|
|
484
|
+
1. 在输入框内发送一条文本消息。
|
|
485
|
+
2. 输入框上的工具栏支持,语音通话、视频通话、图片、视频、文件发送等扩展功能。
|
|
486
|
+
3. 如果您想添加好友,可以前往联系人页面点击"添加好友/群聊"。
|
|
487
|
+
`,
|
|
488
|
+
});
|
|
489
|
+
sendWelcomeMessageOnce.current = true;
|
|
490
|
+
}
|
|
491
|
+
}, 1000);
|
|
492
|
+
return () => clearTimeout(timer);
|
|
493
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
494
|
+
}, [activeConversation?.conversationID]);
|
|
495
|
+
|
|
496
|
+
const openFromChat = (view: 'search' | 'setting') => {
|
|
497
|
+
setPreviousView(activeView);
|
|
498
|
+
setActiveView(view);
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const handleChatBack = () => {
|
|
502
|
+
setActiveConversation(undefined);
|
|
503
|
+
setActiveView('conversations');
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
return (
|
|
507
|
+
<div className="app-shell">
|
|
508
|
+
<Screen active={activeView === 'conversations'}>
|
|
509
|
+
<header className="topbar">
|
|
510
|
+
<span className="topbar__title">
|
|
511
|
+
{t('demo.chats')}{loginUserInfo?.userId ? ` · ${loginUserInfo.userId}` : ''}
|
|
512
|
+
</span>
|
|
513
|
+
<div className="chat-pane__actions">
|
|
514
|
+
<button
|
|
515
|
+
className="topbar__text-btn"
|
|
516
|
+
type="button"
|
|
517
|
+
onClick={() => onChangeLanguage(language === 'zh-CN' ? 'en-US' : 'zh-CN')}
|
|
518
|
+
>
|
|
519
|
+
{language === 'zh-CN' ? 'EN' : '中'}
|
|
520
|
+
</button>
|
|
521
|
+
<button className="topbar__text-btn" type="button" onClick={logout}>{t('demo.logout')}</button>
|
|
522
|
+
</div>
|
|
523
|
+
</header>
|
|
524
|
+
<div className="screen__body">
|
|
525
|
+
<ConversationListH5
|
|
526
|
+
style={{ height: '100%' }}
|
|
527
|
+
onSelectConversation={() => setActiveView('chat')}
|
|
528
|
+
/>
|
|
529
|
+
</div>
|
|
530
|
+
<nav className="tabbar">
|
|
531
|
+
<button className="tabbar__item tabbar__item--active" type="button">{t('demo.chats')}</button>
|
|
532
|
+
<button className="tabbar__item" type="button" onClick={() => setActiveView('contacts')}>{t('demo.contacts')}</button>
|
|
533
|
+
</nav>
|
|
534
|
+
</Screen>
|
|
535
|
+
|
|
536
|
+
<Screen active={activeView === 'contacts'}>
|
|
537
|
+
<header className="topbar">
|
|
538
|
+
<span className="topbar__title">{t('demo.contacts')}</span>
|
|
539
|
+
</header>
|
|
540
|
+
<div className="screen__body">
|
|
541
|
+
<ContactListH5 onContactItemClick={() => setActiveView('contactInfo')} />
|
|
542
|
+
</div>
|
|
543
|
+
<nav className="tabbar">
|
|
544
|
+
<button className="tabbar__item" type="button" onClick={() => setActiveView('conversations')}>{t('demo.chats')}</button>
|
|
545
|
+
<button className="tabbar__item tabbar__item--active" type="button">{t('demo.contacts')}</button>
|
|
546
|
+
</nav>
|
|
547
|
+
</Screen>
|
|
548
|
+
|
|
549
|
+
<Screen active={activeView === 'contactInfo'}>
|
|
550
|
+
<div className="screen__body screen__body--surface">
|
|
551
|
+
<ContactInfoH5
|
|
552
|
+
onClose={() => setActiveView('contacts')}
|
|
553
|
+
onSendMessage={() => setActiveView('chat')}
|
|
554
|
+
onEnterGroup={() => setActiveView('chat')}
|
|
555
|
+
/>
|
|
556
|
+
</div>
|
|
557
|
+
</Screen>
|
|
558
|
+
|
|
559
|
+
<Screen active={activeView === 'chat'}>
|
|
560
|
+
<Chat className="chat-pane">
|
|
561
|
+
<ChatHeaderH5
|
|
562
|
+
className="chat-pane__header"
|
|
563
|
+
enableUserStatus
|
|
564
|
+
onBack={handleChatBack}
|
|
565
|
+
ChatHeaderRight={(
|
|
566
|
+
<div className="chat-pane__actions">
|
|
567
|
+
<button className="icon-btn" type="button" aria-label={t('demo.search')} onClick={() => openFromChat('search')}>
|
|
568
|
+
<IconSearch size="20px" />
|
|
569
|
+
</button>
|
|
570
|
+
<button className="icon-btn" type="button" aria-label={t('demo.setting')} onClick={() => openFromChat('setting')}>
|
|
571
|
+
<IconBulletpoint size="20px" />
|
|
572
|
+
</button>
|
|
573
|
+
</div>
|
|
574
|
+
)}
|
|
575
|
+
/>
|
|
576
|
+
<MessageListH5 />
|
|
577
|
+
<MessageInputH5 />
|
|
578
|
+
</Chat>
|
|
579
|
+
</Screen>
|
|
580
|
+
|
|
581
|
+
<Screen active={activeView === 'search'}>
|
|
582
|
+
<div className="screen__body">
|
|
583
|
+
<SearchH5
|
|
584
|
+
variant={VariantType.EMBEDDED}
|
|
585
|
+
onBack={() => setActiveView(previousView)}
|
|
586
|
+
onResultItemClick={(_item, type) => {
|
|
587
|
+
if (type === 'chat_message') {
|
|
588
|
+
setActiveView('chat');
|
|
589
|
+
}
|
|
590
|
+
}}
|
|
591
|
+
/>
|
|
592
|
+
</div>
|
|
593
|
+
</Screen>
|
|
594
|
+
|
|
595
|
+
<Screen active={activeView === 'setting'}>
|
|
596
|
+
<div className="screen__body screen__body--surface">
|
|
597
|
+
<ChatSettingH5 onBack={() => setActiveView('chat')} />
|
|
598
|
+
</div>
|
|
599
|
+
</Screen>
|
|
600
|
+
</div>
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
[src/ChatLayout.css]
|
|
606
|
+
``` css
|
|
607
|
+
/* ------- Shell + stacked screens ------- */
|
|
608
|
+
.app-shell {
|
|
609
|
+
position: relative;
|
|
610
|
+
width: 100vw;
|
|
611
|
+
height: 100vh;
|
|
612
|
+
height: 100dvh;
|
|
613
|
+
overflow: hidden;
|
|
614
|
+
background: #fff;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* Each full-screen view is stacked; only the active one is visible. */
|
|
618
|
+
.screen {
|
|
619
|
+
position: absolute;
|
|
620
|
+
inset: 0;
|
|
621
|
+
display: flex;
|
|
622
|
+
flex-direction: column;
|
|
623
|
+
background: #fff;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.screen__body {
|
|
627
|
+
flex: 1;
|
|
628
|
+
min-height: 0;
|
|
629
|
+
overflow: hidden;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.screen__body--surface {
|
|
633
|
+
overflow: auto;
|
|
634
|
+
background: #f5f6f8;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/* ------- Top bar ------- */
|
|
638
|
+
.topbar {
|
|
639
|
+
display: flex;
|
|
640
|
+
flex: 0 0 auto;
|
|
641
|
+
min-height: 52px;
|
|
642
|
+
align-items: center;
|
|
643
|
+
justify-content: space-between;
|
|
644
|
+
padding: calc(8px + env(safe-area-inset-top)) 16px 8px;
|
|
645
|
+
border-bottom: 1px solid #e8eaed;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.topbar__title {
|
|
649
|
+
overflow: hidden;
|
|
650
|
+
font-size: 17px;
|
|
651
|
+
font-weight: 600;
|
|
652
|
+
text-overflow: ellipsis;
|
|
653
|
+
white-space: nowrap;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.topbar__text-btn {
|
|
657
|
+
border: 0;
|
|
658
|
+
background: transparent;
|
|
659
|
+
color: #1464ff;
|
|
660
|
+
font-size: 14px;
|
|
661
|
+
font-weight: 500;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* ------- Tab bar ------- */
|
|
665
|
+
.tabbar {
|
|
666
|
+
display: flex;
|
|
667
|
+
flex: 0 0 auto;
|
|
668
|
+
gap: 8px;
|
|
669
|
+
padding: 6px 12px calc(6px + env(safe-area-inset-bottom));
|
|
670
|
+
border-top: 1px solid #e8eaed;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.tabbar__item {
|
|
674
|
+
flex: 1;
|
|
675
|
+
min-height: 40px;
|
|
676
|
+
border: 0;
|
|
677
|
+
border-radius: 12px;
|
|
678
|
+
background: transparent;
|
|
679
|
+
color: #6b7280;
|
|
680
|
+
font-size: 14px;
|
|
681
|
+
font-weight: 600;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.tabbar__item--active {
|
|
685
|
+
background: #eef4ff;
|
|
686
|
+
color: #1464ff;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* ------- Chat pane ------- *
|
|
690
|
+
* The library's <Chat> root is already `display:flex; flex-direction:column;
|
|
691
|
+
* height:100%`, so it fills the screen on its own. The demo only arranges the
|
|
692
|
+
* three slotted children: header (fixed) / message list (grow + scroll) /
|
|
693
|
+
* input (fixed). `min-height:0` on the list breaks the flexbox min-height
|
|
694
|
+
* chain so it scrolls instead of stretching the pane.
|
|
695
|
+
*/
|
|
696
|
+
.chat-pane__header {
|
|
697
|
+
flex: 0 0 auto;
|
|
698
|
+
padding-top: env(safe-area-inset-top);
|
|
699
|
+
border-bottom: 1px solid #e8eaed;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.chat-pane > :nth-child(2) {
|
|
703
|
+
flex: 1;
|
|
704
|
+
min-height: 0;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.chat-pane > :last-child {
|
|
708
|
+
flex: 0 0 auto;
|
|
709
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
710
|
+
border-top: 1px solid #e8eaed;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.chat-pane__actions {
|
|
714
|
+
display: flex;
|
|
715
|
+
align-items: center;
|
|
716
|
+
gap: 4px;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.icon-btn {
|
|
720
|
+
display: inline-flex;
|
|
721
|
+
width: 36px;
|
|
722
|
+
height: 36px;
|
|
723
|
+
align-items: center;
|
|
724
|
+
justify-content: center;
|
|
725
|
+
border: 0;
|
|
726
|
+
border-radius: 10px;
|
|
727
|
+
background: transparent;
|
|
728
|
+
color: #1f2329;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.icon-btn:active {
|
|
732
|
+
background: #eef0f3;
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
6. Configure internationalization.
|
|
737
|
+
|
|
738
|
+
Create **src/i18n.ts** and write the following:
|
|
739
|
+
|
|
740
|
+
``` typescript
|
|
741
|
+
// 本 demo 自身字符串的翻译(聊天组件的翻译由 UIKit 自身
|
|
742
|
+
// 通过 `language` prop 处理)。键名以 `demo.` 作为命名空间,
|
|
743
|
+
// 确保不会与库的 i18next 资源发生冲突。
|
|
744
|
+
//
|
|
745
|
+
// `languageResources` 传递给 <UIKitProvider>;可在任意位置
|
|
746
|
+
// 通过 `const { t } = useUIKit()` 和 `t('demo.chats')` 读取字符串。
|
|
747
|
+
type TranslationTree = { [key: string]: string | TranslationTree };
|
|
748
|
+
interface LanguageResource {
|
|
749
|
+
lng: string;
|
|
750
|
+
translation: TranslationTree;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const en = {
|
|
754
|
+
demo: {
|
|
755
|
+
appTitle: 'Chat UIKit H5',
|
|
756
|
+
field: { sdkAppId: 'SDKAppID', userId: 'UserID', userSig: 'UserSig' },
|
|
757
|
+
placeholder: {
|
|
758
|
+
sdkAppId: 'Your SDKAppID',
|
|
759
|
+
userId: 'e.g. user_001',
|
|
760
|
+
userSig: 'Test UserSig from IM console',
|
|
761
|
+
},
|
|
762
|
+
error: {
|
|
763
|
+
required: 'Please fill in SDKAppID, UserID and UserSig.',
|
|
764
|
+
loginFailed: 'Login failed, please check your credentials.',
|
|
765
|
+
},
|
|
766
|
+
login: 'Login',
|
|
767
|
+
loggingIn: 'Logging in…',
|
|
768
|
+
chats: 'Chats',
|
|
769
|
+
contacts: 'Contacts',
|
|
770
|
+
logout: 'Logout',
|
|
771
|
+
search: 'Search',
|
|
772
|
+
setting: 'Setting',
|
|
773
|
+
},
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
const zh = {
|
|
777
|
+
demo: {
|
|
778
|
+
appTitle: 'Chat UIKit H5',
|
|
779
|
+
field: { sdkAppId: 'SDKAppID', userId: '用户 ID', userSig: '用户签名' },
|
|
780
|
+
placeholder: {
|
|
781
|
+
sdkAppId: '请输入 SDKAppID',
|
|
782
|
+
userId: '例如 user_001',
|
|
783
|
+
userSig: '控制台获取的测试 UserSig',
|
|
784
|
+
},
|
|
785
|
+
error: {
|
|
786
|
+
required: '请填写 SDKAppID、用户 ID 和用户签名。',
|
|
787
|
+
loginFailed: '登录失败,请检查登录凭据。',
|
|
788
|
+
},
|
|
789
|
+
login: '登录',
|
|
790
|
+
loggingIn: '登录中…',
|
|
791
|
+
chats: '会话',
|
|
792
|
+
contacts: '通讯录',
|
|
793
|
+
logout: '退出登录',
|
|
794
|
+
search: '搜索',
|
|
795
|
+
setting: '设置',
|
|
796
|
+
},
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
export const languageResources: LanguageResource[] = [
|
|
800
|
+
{ lng: 'en-US', translation: en },
|
|
801
|
+
{ lng: 'zh-CN', translation: zh },
|
|
802
|
+
];
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### Step 3: Obtain SDKAppID, userID, and userSig
|
|
806
|
+
| Parameter | Type | Description |
|
|
807
|
+
| --- | --- | --- |
|
|
808
|
+
| SDKAppID | Number | The unique identifier for your Tencent Cloud IM application. You can create a new application in the <a href="https://console.cloud.tencent.com/im">IM Console</a> to obtain the SDKAppID.<br><strong>Note:</strong><br>Each independent app should have its own SDKAppID. Messages between different SDKAppIDs are naturally isolated. |
|
|
809
|
+
| userID | String | The unique user identifier you define. Only allows letters (a-z, A-Z), digits (0-9), underscores, and hyphens. |
|
|
810
|
+
| userSig | String | The authentication ticket for logging into IM. It is essentially ciphertext generated by encrypting information such as the UserID.<br><strong>Note:</strong><br>- Development: For quick testing, you can obtain UserSig from the <a href="https://console.cloud.tencent.com/im">IM Console</a>.<br>- Production: Integrate the UserSig calculation on your server. See <a href="https://cloud.tencent.com/document/product/269/32688">Server-side UserSig Generation</a>. |
|
|
811
|
+
|
|
812
|
+
> **Warning:**
|
|
813
|
+
> The sample code in this guide uses UserSig obtained from the [IM Console](https://console.cloud.tencent.com/im), which is **only suitable for local testing and debugging**. For production, see [Server-side UserSig Generation](https://cloud.tencent.com/document/product/269/32688).
|
|
814
|
+
>
|
|
815
|
+
|
|
816
|
+
- SDKAppID: In [IM Console > Application Management](https://console.cloud.tencent.com/im), click **Create New Application** to get the SDKAppID.
|
|
817
|
+
|
|
818
|
+
- userID: Go to [IM Console > Chat > Account Management](https://console.cloud.tencent.com/im/account-management), switch to your target application, and create 2-3 accounts for testing.
|
|
819
|
+
|
|
820
|
+
- userSig: Go to [IM Console > Developer Tools > UserSig Generation](https://console.cloud.tencent.com/im/tool-usersig), switch to your target application, enter the userID, and generate the userSig.
|
|
821
|
+
|
|
822
|
+
## Run and Test
|
|
823
|
+
|
|
824
|
+
Fill in the login credentials and run the project to start experiencing it.
|
|
825
|
+
|
|
826
|
+
[shell]
|
|
827
|
+
``` bash
|
|
828
|
+
npm run dev
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
> **Warning:**
|
|
832
|
+
> - userID and userSig have a one-to-one correspondence. See UserSig Generation for details.
|
|
833
|
+
> - If the project fails to start, check whether the development environment requirements are met.
|
|
834
|
+
|
|
835
|
+
## Integrate Advanced Features
|
|
836
|
+
|
|
837
|
+
### Audio/Video Calls
|
|
838
|
+
|
|
839
|
+
> **Note:**
|
|
840
|
+
> TUICallKit is a Tencent Cloud audio/video calling UI component. By integrating it, you can experience audio/video calling in your chat app with just a few lines of code.
|
|
841
|
+
>
|
|
842
|
+
> For more details, see: Audio/Video Calling - Activate Service.
|
|
843
|
+
>
|
|
844
|
+
|
|
845
|
+
1. Install the `@trtc/calls-uikit-react` dependency.
|
|
846
|
+
|
|
847
|
+
``` bash
|
|
848
|
+
npm install @trtc/calls-uikit-react
|
|
849
|
+
```
|
|
850
|
+
2. Import TUICallKit from `@trtc/calls-uikit-react` and mount it to the DOM.
|
|
851
|
+
|
|
852
|
+
Add the following code to the `src/App.tsx` file:
|
|
853
|
+
|
|
854
|
+
``` typescript
|
|
855
|
+
import { useState } from 'react';
|
|
856
|
+
import { LoginStatus, UIKitProvider, useLoginState } from '@tencentcloud/chat-uikit-react';
|
|
857
|
+
import { TUICallKit } from '@trtc/calls-uikit-react';
|
|
858
|
+
import { LoginView } from './LoginView';
|
|
859
|
+
import { ChatLayout } from './ChatLayout';
|
|
860
|
+
import { languageResources } from './i18n';
|
|
861
|
+
|
|
862
|
+
function App() {
|
|
863
|
+
const { status } = useLoginState();
|
|
864
|
+
const isLogined = status === LoginStatus.SUCCESS;
|
|
865
|
+
// `language` drives both the UIKit's own strings and our demo strings.
|
|
866
|
+
const [language, setLanguage] = useState('en-US');
|
|
867
|
+
|
|
868
|
+
return (
|
|
869
|
+
<UIKitProvider theme="light" language={language} languageResources={languageResources}>
|
|
870
|
+
{isLogined
|
|
871
|
+
? (
|
|
872
|
+
<>
|
|
873
|
+
<ChatLayout onChangeLanguage={setLanguage} />
|
|
874
|
+
{/* Audio/video calls. Mount once at the app root; it shares the chat
|
|
875
|
+
login session, so no extra login is needed. The call buttons are
|
|
876
|
+
already built into the chat header. */}
|
|
877
|
+
<div className="call-kit">
|
|
878
|
+
<TUICallKit />
|
|
879
|
+
</div>
|
|
880
|
+
</>
|
|
881
|
+
)
|
|
882
|
+
: <LoginView language={language} onChangeLanguage={setLanguage} />}
|
|
883
|
+
</UIKitProvider>
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export default App;
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
### Cloud Search
|
|
891
|
+
|
|
892
|
+
> **Note:**
|
|
893
|
+
> Search is an essential feature for customer service, social, online education, telemedicine, OA, and other scenarios. It helps users quickly find groups, users, and messages, improving product experience and user engagement.
|
|
894
|
+
>
|
|
895
|
+
> Due to the limitations of Web platform local storage, **local search is not available**. To better meet search needs, **cloud search** is provided. **Cloud search supports global search and in-conversation search, including searching for groups, users, and messages.**
|
|
896
|
+
>
|
|
897
|
+
> This is a value-added service. You need to purchase the cloud search plugin. Click [Purchase](https://console.cloud.tencent.com/im/plugin/TUICloudSearch).
|
|
898
|
+
>
|
|
899
|
+
|
|
900
|
+
Cloud search is integrated by default in Step 2. To disable cloud search:
|
|
901
|
+
1. Comment out the `ChatHeaderRight` prop in `ChatHeader` and the in-conversation search sidebar related code.
|
|
902
|
+
|
|
903
|
+
``` typescript
|
|
904
|
+
<ChatHeaderH5
|
|
905
|
+
className="chat-pane__header"
|
|
906
|
+
enableUserStatus
|
|
907
|
+
onBack={handleChatBack}
|
|
908
|
+
ChatHeaderRight={(
|
|
909
|
+
<div className="chat-pane__actions">
|
|
910
|
+
{/* <button className="icon-btn" type="button" aria-label={t('demo.search')} onClick={() => openFromChat('search')}>
|
|
911
|
+
<IconSearch size="20px" />
|
|
912
|
+
</button> */}
|
|
913
|
+
<button className="icon-btn" type="button" aria-label={t('demo.setting')} onClick={() => openFromChat('setting')}>
|
|
914
|
+
<IconBulletpoint size="20px" />
|
|
915
|
+
</button>
|
|
916
|
+
</div>
|
|
917
|
+
)}
|
|
918
|
+
/>
|
|
919
|
+
```
|
|
920
|
+
2. Disable cloud search on the `ConversationList` component.
|
|
921
|
+
|
|
922
|
+
``` java
|
|
923
|
+
<ConversationListH5 enableSearch={false} enableCreate={false} />
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
## FAQ
|
|
927
|
+
|
|
928
|
+
### What is UserSig? How to generate UserSig?
|
|
929
|
+
|
|
930
|
+
UserSig is the authentication ticket for logging into IM. It is essentially ciphertext generated by encrypting information such as the UserID. The recommended approach is to integrate UserSig calculation on your server and provide a project-facing API. For more details, see [Server-side UserSig Generation](https://cloud.tencent.com/document/product/269/32688).
|
|
931
|
+
|
|
932
|
+
> **Warning:**
|
|
933
|
+
> The sample code in this guide uses UserSig obtained from the [IM Console](https://console.cloud.tencent.com/im), which is **only suitable for local testing and debugging**. For production, see [Server-side UserSig Generation](https://cloud.tencent.com/document/product/269/32688).
|
|
934
|
+
>
|
|
935
|
+
|
|
936
|
+
### Is React 17 supported?
|
|
937
|
+
|
|
938
|
+
React v17.x is not supported. Only React v18.2+ and above are supported.
|
|
939
|
+
|
|
940
|
+
### Can I use third-party component libraries like Ant-Design?
|
|
941
|
+
|
|
942
|
+
You can use other component libraries for the glue code between core components, as shown in the example code — for instance, you can wrap `<ChatSettingH5 />` in a full-screen drawer component. However, components already built into the core components cannot be modified at this time.
|
|
943
|
+
``` typescript
|
|
944
|
+
import { Drawer } from 'antd';
|
|
945
|
+
|
|
946
|
+
const [isChatSettingShow, setIsChatSettingShow] = useState(false);
|
|
947
|
+
|
|
948
|
+
function onChatSettingClose() {
|
|
949
|
+
setIsChatSettingShow(false);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
<Drawer
|
|
953
|
+
title="设置"
|
|
954
|
+
open={isChatSettingShow}
|
|
955
|
+
onClose={onChatSettingClose}
|
|
956
|
+
>
|
|
957
|
+
<ChatSettingH5 />
|
|
958
|
+
</Drawer>
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Emoji Packs
|
|
962
|
+
|
|
963
|
+
To respect copyrights, the IM Demo/TUIKit project does not include large emoji sticker assets by default. Before going live, please replace them with your own designed or licensed emoji packs. The **default yellow-face emoji pack shown below is copyrighted by Tencent Cloud**. You can use it for free by upgrading to the [IM Enterprise plan](https://buy.cloud.tencent.com/avc).
|
|
964
|
+
|