| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- import React, { useState, useEffect } from 'react';
- // import 'https://g.alicdn.com/apsara-media-box/imp-interaction/1.6.1/alivc-im.iife.js'
- // 从 SDK 中提取需要的类型
- type ImEngine = InstanceType<typeof AliVCInteraction.ImEngine>;
- type ImGroupManager = AliVCInteraction.AliVCIMGroupManager;
- type ImMessageManager = AliVCInteraction.AliVCIMMessageManager;
- type ImLogLevel = AliVCInteraction.ImLogLevel;
- type ImMessageLevel = AliVCInteraction.ImMessageLevel;
- interface ImUser {
- userId: string;
- userExtension?: string;
- }
- interface ImGroupMessage {
- groupId: string;
- type: number;
- data: string;
- sender?: ImUser;
- timestamp?: number;
- }
- interface ImGroupMemberChangeEvent {
- groupId: string;
- memberCount: number;
- joinUsers: ImUser[];
- leaveUsers: ImUser[];
- }
- interface ImGroupMuteChangeEvent {
- groupId: string;
- status: AliVCInteraction.ImGroupMuteStatus;
- }
- interface ImGroupInfoChangeEvent {
- groupId: string;
- info: AliVCInteraction.ImGroupInfoStatus;
- }
- interface ImError {
- code: number;
- msg: string;
- message?: string;
- }
- const { ImEngine: ImEngineClass, ImLogLevel, ImMessageLevel } = window.AliVCInteraction;
- // 请从控制台复制对应的值填入下面 AppId、 AppKey、AppSign 变量当中
- // 注意:这里仅作为本地快速体验使用,实际开发请勿在前端泄露 AppKey
- const AppId = '4c2ab5e1b1b0';
- const AppKey = '314bb5eee5b623549e8a41574ba3ff32';
- const AppSign = 'H4sIAAAAAAAE/wCQAG//zguHB+lYCilkv7diSkk4GmcvLuds+InRu9vFOFebMwm/jEgsK5bBT85Z0owObMxG58uXHyPFlPEBEDQm9FswNJ+KmX0VDYkcfdPPWkafA6Hc0B6F+p5De9yJfPEfHzwo/DHMaygbHfLmBgUtmKveq421sJr/gNBz9D04Ewsg39us+ao0NegzLt7xtXvFXXXJAAAA//8BAAD//yoav6aQAAAA';
- export const LivePage = () => {
- const [userId, setUserId] = useState<string>('');
- const [groupId, setGroupId] = useState<string>('');
- const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
- const [isJoinedGroup, setIsJoinedGroup] = useState<boolean>(false);
- const [msgText, setMsgText] = useState<string>('');
- const [messageList, setMessageList] = useState<string[]>([]);
- const [errorMessage, setErrorMessage] = useState<string>('');
- const engine: ImEngine = ImEngineClass.createEngine();
- const [groupManager, setGroupManager] = useState<ImGroupManager | null>(null);
- const [messageManager, setMessageManager] = useState<ImMessageManager | null>(null);
- const [joinedGroupId, setJoinedGroupId] = useState<string | null>(null);
- const sha256 = async (message: string): Promise<string> => {
- const encoder = new TextEncoder();
- const data = encoder.encode(message);
- const result = await crypto.subtle.digest('SHA-256', data).then((buffer) => {
- let hash = Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('');
- return hash;
- });
- return result;
- };
- const getLoginAuth = async (userId: string, role: string): Promise<AliVCInteraction.ImAuth> => {
- const nonce = 'AK_4';
- const timestamp = Math.floor(Date.now() / 1000) + 3600 * 3;
- const pendingShaStr = `${AppId}${AppKey}${userId}${nonce}${timestamp}${role}`;
- const appToken = await sha256(pendingShaStr);
- return {
- nonce,
- timestamp,
- token: appToken,
- role,
- };
- };
- const showMessage = (text: string): void => {
- setMessageList([...messageList, text]);
- };
- const listenEngineEvents = (): void => {
- engine.on('connecting', () => {
- console.log('connecting');
- });
- engine.on('connectfailed', (err) => {
- console.log(`connect failed: ${err.message}`);
- });
- engine.on('connectsuccess', () => {
- console.log('connect success');
- });
- engine.on('disconnect', (code: number) => {
- console.log(`disconnect: ${code}`);
- });
- engine.on('tokenexpired', async (cb: (error: null, auth: AliVCInteraction.ImAuth) => void) => {
- console.log('token expired');
- // 这里需要更新为获取新的登录信息的代码
- // const auth = await getLoginAuth(userId, role);
- // cb(null, auth);
- });
- };
- const listenGroupEvents = (): void => {
- if (!groupManager) {
- return;
- }
- groupManager.on('exit', (groupId: string, reason: number) => {
- showMessage(`group ${groupId} close, reason: ${reason}`);
- });
- groupManager.on('memberchange', (groupId: string, memberCount: number, joinUsers: ImUser[], leaveUsers: ImUser[]) => {
- showMessage(`group ${groupId} member change, memberCount: ${memberCount}, joinUsers: ${joinUsers.map(u => u.userId).join(',')}, leaveUsers: ${leaveUsers.map(u => u.userId).join('')}`);
- });
- groupManager.on('mutechange', (groupId: string, status: AliVCInteraction.ImGroupMuteStatus) => {
- showMessage(`group ${groupId} mute change`);
- });
- groupManager.on('infochange', (groupId: string, info: AliVCInteraction.ImGroupInfoStatus) => {
- showMessage(`group ${groupId} info change`);
- });
- };
- const listenMessageEvents = (): void => {
- if (!messageManager) {
- return;
- }
- messageManager.on('recvgroupmessage', (msg: AliVCInteraction.ImMessage, groupId: string) => {
- console.log('recvgroupmessage', msg, groupId);
- showMessage(`receive group: ${groupId}, type: ${msg.type}, data: ${msg.data}`);
- });
- };
- const login = async (userId: string): Promise<void> => {
- try {
- await engine.init({
- deviceId: 'xxxx',
- appId: AppId,
- appSign: AppSign,
- logLevel: ImLogLevel.ERROR,
- });
- listenEngineEvents();
- const role = 'admin';
- const authData = await getLoginAuth(userId, role);
- await engine.login({
- user: {
- userId,
- userExtension: '{}',
- },
- userAuth: {
- timestamp: authData.timestamp,
- nonce: authData.nonce,
- role: authData.role,
- token: authData.token,
- },
- });
- const gm = engine.getGroupManager();
- const mm = engine.getMessageManager();
- setGroupManager(gm || null);
- setMessageManager(mm || null);
- setIsLoggedIn(true);
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`初始化、登录失败: ${err.code} ${err.msg}`);
- }
- };
- const logout = async (): Promise<void> => {
- try {
- await engine.logout();
- engine.unInit();
- setGroupManager(null);
- setMessageManager(null);
- setJoinedGroupId(null);
- setIsLoggedIn(false);
- setIsJoinedGroup(false);
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`登出失败: ${err.code} ${err.msg}`);
- }
- };
- const joinGroup = async (groupId: string): Promise<void> => {
- try {
- if (!groupManager) {
- return;
- }
- await groupManager.joinGroup(groupId);
- setJoinedGroupId(groupId);
- listenGroupEvents();
- listenMessageEvents();
- setIsJoinedGroup(true);
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`加入群组失败: ${err.code} ${err.msg}`);
- }
- };
- const leaveGroup = async (): Promise<void> => {
- try {
- if (!groupManager || !joinedGroupId) {
- return;
- }
- await groupManager.leaveGroup(joinedGroupId);
- groupManager.removeAllListeners();
- messageManager?.removeAllListeners();
- setJoinedGroupId(null);
- setIsJoinedGroup(false);
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`离开群组失败: ${err.code} ${err.msg}`);
- }
- };
- const createGroup = async (): Promise<void> => {
- try {
- if (!groupManager) {
- return;
- }
- const res = await groupManager.createGroup({
- groupId,
- groupName: 'xxx',
- groupMeta: 'xxx',
- });
- console.log('创建群组成功', res);
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`创建群组失败: ${err.code} ${err.msg}`);
- }
- };
- const sendMessage = async (): Promise<void> => {
- try {
- if (!messageManager || !joinedGroupId) {
- return;
- }
- const res = await messageManager.sendGroupMessage({
- groupId: joinedGroupId,
- data: msgText,
- type: 88888,
- skipAudit: false,
- skipMuteCheck: false,
- level: ImMessageLevel.NORMAL,
- noStorage: true,
- repeatCount: 1,
- });
- console.log('群消息发送成功', res);
- setMsgText('');
- setErrorMessage('');
- } catch (err: any) {
- setErrorMessage(`群消息发送失败: ${err.code} ${err.msg}`);
- }
- };
- const clearMessages = (): void => {
- setMessageList([]);
- };
- useEffect(() => {
- return () => {
- if (groupManager) {
- groupManager.removeAllListeners();
- }
- if (messageManager) {
- messageManager.removeAllListeners();
- }
- if (engine) {
- engine.removeAllListeners();
- }
- };
- }, []);
- return (
- <div className="container mx-auto p-4">
- <h1 className="text-2xl font-bold mb-4">互动消息 quick start</h1>
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
- <div>
- <form>
- <div className="mb-2">
- <label htmlFor="userId" className="block text-sm font-medium text-gray-700">用户ID</label>
- <input
- className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
- id="userId"
- placeholder="请输入英文字母或数字"
- value={userId}
- onChange={(e) => setUserId(e.target.value)}
- />
- </div>
- <div className="mb-2">
- <label htmlFor="groupId" className="block text-sm font-medium text-gray-700">群组ID</label>
- <input
- className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
- id="groupId"
- placeholder="加入群组前请确认是否已存在,不存在先创建"
- value={groupId}
- onChange={(e) => setGroupId(e.target.value)}
- />
- </div>
- <div className="mb-2 flex space-x-2">
- <button
- id="loginBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${isLoggedIn? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={isLoggedIn}
- onClick={() => login(userId)}
- >
- 登录
- </button>
- <button
- id="joinBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${isLoggedIn && isJoinedGroup? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={isLoggedIn && isJoinedGroup}
- onClick={() => joinGroup(groupId)}
- >
- 加入群组
- </button>
- <button
- id="createBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isLoggedIn? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={!isLoggedIn}
- onClick={() => createGroup()}
- >
- 创建群组
- </button>
- <button
- id="leaveBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 ${!isLoggedIn ||!isJoinedGroup? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={!isLoggedIn ||!isJoinedGroup}
- onClick={() => leaveGroup()}
- >
- 离开群组
- </button>
- <button
- id="logoutBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 ${!isLoggedIn? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={!isLoggedIn}
- onClick={() => logout()}
- >
- 登出
- </button>
- </div>
- <p className="mb-2 text-sm text-gray-600">假如群组ID已存在,可以一键登录+加入群组</p>
- <div className="mb-2 flex space-x-2">
- <button
- id="oneLoginBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${isLoggedIn && isJoinedGroup? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={isLoggedIn && isJoinedGroup}
- onClick={() => {
- if (userId && groupId) {
- login(userId).then(() => {
- joinGroup(groupId);
- });
- }
- }}
- >
- 一键登录+加入群组
- </button>
- <button
- id="oneLogoutBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 ${!isLoggedIn ||!isJoinedGroup? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={!isLoggedIn ||!isJoinedGroup}
- onClick={() => {
- leaveGroup().then(() => {
- logout();
- });
- }}
- >
- 一键离开群组+登出
- </button>
- </div>
- </form>
- <form className="mt-4">
- <div className="mb-2">
- <label htmlFor="msgText" className="block text-sm font-medium text-gray-700">消息</label>
- <input
- className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
- id="msgText"
- value={msgText}
- onChange={(e) => setMsgText(e.target.value)}
- />
- </div>
- <div className="mb-2">
- <button
- id="sendBtn"
- type="button"
- className={`px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isLoggedIn ||!isJoinedGroup? 'opacity-50 cursor-not-allowed' : ''}`}
- disabled={!isLoggedIn ||!isJoinedGroup}
- onClick={() => sendMessage()}
- >
- 发送
- </button>
- </div>
- </form>
- </div>
- <div>
- <h5 className="flex justify-between items-center text-lg font-medium mb-2">
- 消息展示
- <button
- id="clearBtn"
- type="button"
- className="px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
- onClick={clearMessages}
- >
- 清空
- </button>
- </h5>
- <div id="msgList" className="mt-4 space-y-2">
- {messageList.map((msg, index) => (
- <div key={index} className="bg-gray-100 p-2 rounded-md">{msg}</div>
- ))}
- </div>
- </div>
- </div>
- {errorMessage && <div className="mt-2 text-red-500">{errorMessage}</div>}
- </div>
- );
- };
|