|
|
@@ -1,434 +0,0 @@
|
|
|
-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>
|
|
|
- );
|
|
|
-};
|