|
|
@@ -72,6 +72,14 @@ export enum ClassStatus {
|
|
|
}
|
|
|
|
|
|
|
|
|
+// 定义消息类型
|
|
|
+export interface Message {
|
|
|
+ type: 'text' | 'image' | 'system';
|
|
|
+ content: string;
|
|
|
+ sender?: string;
|
|
|
+ timestamp: number;
|
|
|
+}
|
|
|
+
|
|
|
export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
// 状态管理
|
|
|
// const [userId, setUserId] = useState<string>(''); // 保持string类型
|
|
|
@@ -85,7 +93,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
|
|
|
const [isJoinedClass, setIsJoinedClass] = useState<boolean>(false);
|
|
|
const [msgText, setMsgText] = useState<string>('');
|
|
|
- const [messageList, setMessageList] = useState<string[]>([]);
|
|
|
+ const [messageList, setMessageList] = useState<Message[]>([]);
|
|
|
const [errorMessage, setErrorMessage] = useState<string>('');
|
|
|
const [classStatus, setClassStatus] = useState<ClassStatus>(ClassStatus.NOT_STARTED);
|
|
|
const [handUpList, setHandUpList] = useState<HandUpRequest[]>([]);
|
|
|
@@ -106,12 +114,33 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const remoteCameraContainer = useRef<HTMLDivElement>(null); // 摄像头小窗容器
|
|
|
|
|
|
// 辅助函数
|
|
|
- const showMessage = (text: string): void => {
|
|
|
- setMessageList((prevMessageList) => [...prevMessageList, text])
|
|
|
+ const showMessage = (text: string, sender?: string): void => {
|
|
|
+ const message: Message = {
|
|
|
+ type: 'text',
|
|
|
+ content: text,
|
|
|
+ sender,
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+ setMessageList((prevMessageList) => [...prevMessageList, message])
|
|
|
};
|
|
|
|
|
|
const showImageMessage = (imageUrl: string, senderName: string): void => {
|
|
|
- setMessageList((prevMessageList) => [...prevMessageList, `[图片] ${senderName}: ${imageUrl}`])
|
|
|
+ const message: Message = {
|
|
|
+ type: 'image',
|
|
|
+ content: imageUrl,
|
|
|
+ sender: senderName,
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+ setMessageList((prevMessageList) => [...prevMessageList, message])
|
|
|
+ };
|
|
|
+
|
|
|
+ const showSystemMessage = (text: string): void => {
|
|
|
+ const message: Message = {
|
|
|
+ type: 'system',
|
|
|
+ content: text,
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+ setMessageList((prevMessageList) => [...prevMessageList, message])
|
|
|
};
|
|
|
|
|
|
const showToast = (type: 'info' | 'success' | 'error', message: string): void => {
|
|
|
@@ -133,11 +162,11 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
if (!role) return;
|
|
|
|
|
|
imEngine.current.on('connectsuccess', () => {
|
|
|
- showMessage('IM连接成功');
|
|
|
+ showSystemMessage('IM连接成功');
|
|
|
});
|
|
|
|
|
|
imEngine.current.on('disconnect', async (code: number) => {
|
|
|
- showMessage(`IM断开连接: ${code}`);
|
|
|
+ showSystemMessage(`IM断开连接: ${code}`);
|
|
|
// 自动重连
|
|
|
try {
|
|
|
const res = await aliyunClient.im_token.$post({
|
|
|
@@ -160,10 +189,10 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
role
|
|
|
}
|
|
|
});
|
|
|
- showMessage('IM自动重连成功');
|
|
|
+ showSystemMessage('IM自动重连成功');
|
|
|
} catch (err: unknown) {
|
|
|
const error = err as Error;
|
|
|
- showMessage(`IM自动重连失败: ${error.message}`);
|
|
|
+ showSystemMessage(`IM自动重连失败: ${error.message}`);
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -172,7 +201,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
if (!imGroupManager.current) return;
|
|
|
|
|
|
imGroupManager.current.on('memberchange', (groupId: string, memberCount: number, joinUsers: ImUser[], leaveUsers: ImUser[]) => {
|
|
|
- showMessage(`成员变更: 加入${joinUsers.length}人, 离开${leaveUsers.length}人`);
|
|
|
+ showSystemMessage(`成员变更: 加入${joinUsers.length}人, 离开${leaveUsers.length}人`);
|
|
|
|
|
|
// 更新学生列表
|
|
|
setStudents(prevStudents => {
|
|
|
@@ -229,17 +258,17 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const data = JSON.parse(msg.data);
|
|
|
if (data.action === 'start_class') {
|
|
|
setClassStatus(ClassStatus.IN_PROGRESS);
|
|
|
- showMessage('老师已开始上课');
|
|
|
+ showSystemMessage('老师已开始上课');
|
|
|
} else if (data.action === 'end_class') {
|
|
|
setClassStatus(ClassStatus.ENDED);
|
|
|
- showMessage('老师已结束上课');
|
|
|
+ showSystemMessage('老师已结束上课');
|
|
|
|
|
|
// 学生端收到结束课堂消息后,立即自动离开群组
|
|
|
if (role === Role.Student) {
|
|
|
try {
|
|
|
if (imGroupManager.current && classId) {
|
|
|
await imGroupManager.current.leaveGroup(classId);
|
|
|
- showMessage('已自动离开课堂群组');
|
|
|
+ showSystemMessage('已自动离开课堂群组');
|
|
|
}
|
|
|
if (aliRtcEngine.current) {
|
|
|
await leaveRtcChannel();
|
|
|
@@ -263,7 +292,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
try {
|
|
|
const data = JSON.parse(msg.data);
|
|
|
if (data.action === 'toggle_mute' && data.userId === userId) {
|
|
|
- showMessage(data.mute ? '你已被老师静音' : '老师已取消你的静音');
|
|
|
+ showSystemMessage(data.mute ? '你已被老师静音' : '老师已取消你的静音');
|
|
|
}
|
|
|
} catch (err) {
|
|
|
console.error('解析静音指令失败', err);
|
|
|
@@ -277,7 +306,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
timestamp: data.timestamp || Date.now()
|
|
|
};
|
|
|
setHandUpList([...handUpList, handUpData]);
|
|
|
- showMessage(`${data.studentName || data.studentId} 举手了`);
|
|
|
+ showSystemMessage(`${data.studentName || data.studentId} 举手了`);
|
|
|
} else if (data.action === InteractionAction.CancelHandUp) {
|
|
|
setHandUpList(handUpList.filter(h => h.studentId !== data.studentId));
|
|
|
}
|
|
|
@@ -302,7 +331,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
};
|
|
|
setQuestions([...questions, question]);
|
|
|
}
|
|
|
- showMessage(`收到问题: ${data.question}`);
|
|
|
+ showSystemMessage(`收到问题: ${data.question}`);
|
|
|
} catch (err) {
|
|
|
console.error('解析问题消息失败', err);
|
|
|
}
|
|
|
@@ -310,7 +339,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
try {
|
|
|
const data = JSON.parse(msg.data) as InteractionMessage;
|
|
|
if (data.action === InteractionAction.AnswerHandUp && data.studentId === userId) {
|
|
|
- showMessage('老师已应答你的举手');
|
|
|
+ showSystemMessage('老师已应答你的举手');
|
|
|
setHandUpList(handUpList.filter(h => h.studentId !== data.studentId));
|
|
|
}
|
|
|
} catch (err) {
|
|
|
@@ -320,7 +349,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
try {
|
|
|
const data = JSON.parse(msg.data) as InteractionMessage;
|
|
|
if (data.action === InteractionAction.KickStudent && data.studentId === userId) {
|
|
|
- showMessage('您已被老师移出课堂');
|
|
|
+ showSystemMessage('您已被老师移出课堂');
|
|
|
// 学生被踢出后自动离开课堂
|
|
|
setTimeout(() => {
|
|
|
leaveClass();
|
|
|
@@ -334,7 +363,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
// 使用正确的 ImUser 类型处理发送者信息
|
|
|
const userExtension = sender?.userExtension ? JSON.parse(sender.userExtension) : {};
|
|
|
const senderName = userExtension.nickname || userExtension.username || sender?.userId || '未知用户';
|
|
|
- showMessage(`${senderName}: ${msg.data}`);
|
|
|
+ showMessage(msg.data, senderName);
|
|
|
} else if (msg.type === 88895) { // 图片消息
|
|
|
try {
|
|
|
const data = JSON.parse(msg.data);
|
|
|
@@ -350,12 +379,12 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const fileInfo = await response.json();
|
|
|
showImageMessage(fileInfo.fullUrl, senderName);
|
|
|
} else {
|
|
|
- showMessage(`${senderName}: [图片] (获取失败)`);
|
|
|
+ showSystemMessage(`${senderName}: 图片获取失败`);
|
|
|
}
|
|
|
}
|
|
|
} catch (err) {
|
|
|
console.error('解析图片消息失败', err);
|
|
|
- showMessage('收到一张图片(解析失败)');
|
|
|
+ showSystemMessage('图片消息解析失败');
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
@@ -382,7 +411,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const listenRtcEvents = () => {
|
|
|
if (!aliRtcEngine.current) return;
|
|
|
|
|
|
- showMessage('注册rtc事件监听')
|
|
|
+ showSystemMessage('注册rtc事件监听')
|
|
|
|
|
|
aliRtcEngine.current.on('remoteUserOnLineNotify', (userId: string) => {
|
|
|
// showMessage(`用户 ${userId} 加入课堂`);
|
|
|
@@ -435,16 +464,16 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
);
|
|
|
|
|
|
console.log(`已订阅用户 ${userId} 的视频流`);
|
|
|
- showMessage(`已显示用户 ${userId} 的视频`);
|
|
|
+ showSystemMessage(`已显示用户 ${userId} 的视频`);
|
|
|
} catch (err) {
|
|
|
console.error(`订阅用户 ${userId} 视频流失败:`, err);
|
|
|
- showMessage(`订阅用户 ${userId} 视频流失败`);
|
|
|
+ showSystemMessage(`订阅用户 ${userId} 视频流失败`);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 1: // 取消订阅
|
|
|
console.log(`取消订阅用户 ${userId} 的视频流`);
|
|
|
- showMessage(`取消订阅用户 ${userId} 的视频流`);
|
|
|
+ showSystemMessage(`取消订阅用户 ${userId} 的视频流`);
|
|
|
removeRemoteVideo(userId, 'camera');
|
|
|
break;
|
|
|
|
|
|
@@ -496,16 +525,16 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
);
|
|
|
|
|
|
console.log(`已订阅用户 ${userId} 的屏幕分享流`);
|
|
|
- showMessage(`已显示用户 ${userId} 的屏幕分享`);
|
|
|
+ showSystemMessage(`已显示用户 ${userId} 的屏幕分享`);
|
|
|
} catch (err) {
|
|
|
console.error(`订阅用户 ${userId} 屏幕分享流失败:`, err);
|
|
|
- showMessage(`订阅用户 ${userId} 屏幕分享流失败`);
|
|
|
+ showSystemMessage(`订阅用户 ${userId} 屏幕分享流失败`);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 1: // 取消订阅
|
|
|
console.log(`取消订阅用户 ${userId} 的屏幕分享流`);
|
|
|
- showMessage(`取消订阅用户 ${userId} 的屏幕分享流`);
|
|
|
+ showSystemMessage(`取消订阅用户 ${userId} 的屏幕分享流`);
|
|
|
removeRemoteVideo(userId, 'screen');
|
|
|
break;
|
|
|
|
|
|
@@ -607,11 +636,11 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
});
|
|
|
|
|
|
setStudents(existingMembers);
|
|
|
- showMessage(`已加载 ${existingMembers.length} 名现有学生`);
|
|
|
+ showSystemMessage(`已加载 ${existingMembers.length} 名现有学生`);
|
|
|
}
|
|
|
} catch (err) {
|
|
|
console.error('获取群组成员失败:', err);
|
|
|
- showMessage('获取现有学生列表失败,将只显示新加入的学生');
|
|
|
+ showSystemMessage('获取现有学生列表失败,将只显示新加入的学生');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -711,9 +740,6 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
type: 88895, // 使用单一消息类型用于图片
|
|
|
level: NORMAL,
|
|
|
});
|
|
|
-// 发送方不需要手动显示消息,IM系统会自动将消息广播给所有用户(包括发送方自己)
|
|
|
-// 消息接收处理逻辑(第338-360行)会自动处理并显示图片消息
|
|
|
-
|
|
|
|
|
|
} catch (err: any) {
|
|
|
// 检查是否为禁言错误 (错误码442)
|
|
|
@@ -769,7 +795,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
// 3. 关闭群组(删除群组)
|
|
|
if (imGroupManager.current) {
|
|
|
await imGroupManager.current.closeGroup(classId);
|
|
|
- showMessage('群组已关闭');
|
|
|
+ showSystemMessage('群组已关闭');
|
|
|
|
|
|
// 4. 清理状态
|
|
|
setIsJoinedClass(false);
|
|
|
@@ -859,7 +885,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
await gm.joinGroup(response.groupId);
|
|
|
|
|
|
showToast('success', '课堂创建并加入成功');
|
|
|
- showMessage(`课堂 ${className} 创建成功,ID: ${response.groupId}`);
|
|
|
+ showSystemMessage(`课堂 ${className} 创建成功,ID: ${response.groupId}`);
|
|
|
|
|
|
setClassId(response.groupId);
|
|
|
setIsJoinedClass(true);
|