Commit 8cb42e29 authored by liujiaxin's avatar liujiaxin

输入修改,老师全部静音的时候学生无权开启

parent 0c1868b2
...@@ -234,7 +234,7 @@ const MemberHolder: React.FC<MemberHolderProps> = ({ ...@@ -234,7 +234,7 @@ const MemberHolder: React.FC<MemberHolderProps> = ({
{/*me.uid === id ||*/} {/*me.uid === id ||*/}
{me.role === 'teacher' ? {me.role === 'teacher' ?
<span className="media-btn"> <span className="media-btn">
<Link component="button" onClick={onAudioClick}>{audio ? "静音" : "开麦"}</Link> {/*<Link component="button" onClick={onAudioClick}>{audio ? "静音" : "开麦"}</Link>*/}
{me.role === 'teacher' ? <Link component="button" onClick={onStageUp}>上台</Link> : null} {me.role === 'teacher' ? <Link component="button" onClick={onStageUp}>上台</Link> : null}
{/*<Icon onClick={onAudioClick} className={audio ? "icon-speaker-on" : "icon-speaker-off"} data={"audio"} />*/} {/*<Icon onClick={onAudioClick} className={audio ? "icon-speaker-on" : "icon-speaker-off"} data={"audio"} />*/}
{/*<Icon onClick={onVideoClick} className={video ? "icons-camera-unmute-s" : "icons-camera-mute-s"} data={"video"} />*/} {/*<Icon onClick={onVideoClick} className={video ? "icons-camera-unmute-s" : "icons-camera-mute-s"} data={"video"} />*/}
......
...@@ -43,12 +43,12 @@ export default function RoomToolsBar (props: any) { ...@@ -43,12 +43,12 @@ export default function RoomToolsBar (props: any) {
muteAudio: roomState.course.muteAudio muteAudio: roomState.course.muteAudio
} }
}, [roomState.course]); }, [roomState.course]);
const audioOn = async () => { const allMuteOn = async () => {
console.log('audioOn'); console.log('allStudentMuteOn');
await roomStore.allStudentMuteOn() await roomStore.allStudentMuteOn()
} }
const audioOff = async () => { const allMuteOff = async () => {
console.log('audioOff'); console.log('allStudentMuteOff');
await roomStore.allStudentMuteOff() await roomStore.allStudentMuteOff()
} }
const randomStageUp = async () => { const randomStageUp = async () => {
...@@ -61,9 +61,10 @@ export default function RoomToolsBar (props: any) { ...@@ -61,9 +61,10 @@ export default function RoomToolsBar (props: any) {
{roomStore.state.me.role == 'teacher' ? {roomStore.state.me.role == 'teacher' ?
<> <>
{/*<div className="tool-btn" onClick={audioOn}><VoiceOverOffIcon/></div>*/}
{muteAudio {muteAudio
? <div className="tool-btn" onClick={audioOn}><VoiceOverOffIcon/></div> ? <div className="tool-btn" onClick={allMuteOff}><RecordVoiceOverIcon/></div>
: <div onClick={audioOff} className="tool-btn"><RecordVoiceOverIcon/></div> : <div onClick={allMuteOn} className="tool-btn"><VoiceOverOffIcon/></div>
} }
{currentTool == TOOL_TYPE.WHITEBOARD {currentTool == TOOL_TYPE.WHITEBOARD
? <div className="tool-btn" onClick={changeTool}><ImportContacts/></div> ? <div className="tool-btn" onClick={changeTool}><ImportContacts/></div>
......
...@@ -6,6 +6,7 @@ import { useRoomState } from '../containers/root-container'; ...@@ -6,6 +6,7 @@ import { useRoomState } from '../containers/root-container';
import { platform } from '../utils/platform'; import { platform } from '../utils/platform';
import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward';
import {makeStyles} from '@material-ui/core/styles'; import {makeStyles} from '@material-ui/core/styles';
import {roomStore} from "../stores/room";
const useStyles = makeStyles({ const useStyles = makeStyles({
stageDown : { stageDown : {
...@@ -40,6 +41,8 @@ interface VideoPlayerProps { ...@@ -40,6 +41,8 @@ interface VideoPlayerProps {
handleClose?: (uid: string, streamID: number) => void handleClose?: (uid: string, streamID: number) => void
} }
let reloadStreamTimer: number | null = null;
const VideoPlayer: React.FC<VideoPlayerProps> = ({ const VideoPlayer: React.FC<VideoPlayerProps> = ({
preview, preview,
role, role,
...@@ -60,6 +63,11 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ...@@ -60,6 +63,11 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const loadVideo = useRef<boolean>(false); const loadVideo = useRef<boolean>(false);
const loadAudio = useRef<boolean>(false); const loadAudio = useRef<boolean>(false);
const stateError = useRef<boolean>(false);
// const hasError = useMemo(() => {
// return stateError.current;
// }, [stateError]);
const lockPlay = useRef<boolean>(false); const lockPlay = useRef<boolean>(false);
const AgoraRtcEngine = useMemo(() => { const AgoraRtcEngine = useMemo(() => {
...@@ -130,15 +138,53 @@ useEffect(() => { ...@@ -130,15 +138,53 @@ useEffect(() => {
if (platform === 'web') { if (platform === 'web') {
if (!stream || !domId || lockPlay.current && stream.isPlaying()) return; if (!stream || !domId || lockPlay.current && stream.isPlaying()) return;
lockPlay.current = true; lockPlay.current = true;
/*const playFn = () => {
if (reloadStreamTimer) {
clearTimeout(reloadStreamTimer)
reloadStreamTimer = null;
}
reloadStreamTimer = window.setTimeout(() => {
stream.play(`${domId}`, { fit: 'cover' }, (err: any) => {
console.log('reloadStreamTimer reloadStreamTimer reloadStreamTimer')
lockPlay.current = false;
if (err && err.status !== 'aborted') {
// 播放失败,一般为浏览器策略阻止。引导用户用手势触发恢复播放。
console.warn('[video-player] ', err, id);
if (stream.isPlaying()) {
stream.stop();
}
playFn();
}
})
}, 1000)
}*/
stream.play(`${domId}`, { fit: 'cover' }, (err: any) => { stream.play(`${domId}`, { fit: 'cover' }, (err: any) => {
// console.log('reloadStreamTimer reloadStreamTimer reloadStreamTimer')
lockPlay.current = false; lockPlay.current = false;
if (err && err.status !== 'aborted') { if (err && err.status !== 'aborted') {
lockPlay.current = true;
// 播放失败,一般为浏览器策略阻止。引导用户用手势触发恢复播放。
console.warn('[video-player] ', err, id); console.warn('[video-player] ', err, id);
stateError.current = true;
// @ts-ignore
window.errorPlayer = stream;
} }
}) })
return () => { return () => {
console.log(2343454567,local, streamID, me)
if (reloadStreamTimer) {
clearTimeout(reloadStreamTimer)
reloadStreamTimer = null;
}
if (stream.isPlaying()) { if (stream.isPlaying()) {
stream.stop(); // debugger
try{
stream.stop();
} catch (e) {
// console.log(e);
}
// stream.stop();
} }
local && stream && stream.close(); local && stream && stream.close();
} }
...@@ -253,6 +299,10 @@ const onClose = (evt: any) => { ...@@ -253,6 +299,10 @@ const onClose = (evt: any) => {
handleClose('close', streamID); handleClose('close', streamID);
} }
} }
const resumeStream = (evt: any) => {
stateError.current = false;
stream.resume()
}
const me = useRoomState().me; const me = useRoomState().me;
...@@ -264,18 +314,22 @@ return ( ...@@ -264,18 +314,22 @@ return (
account ? account ?
<div className="video-profile"> <div className="video-profile">
<span className="account">{account}</span> <span className="account">{account}</span>
{/*{stateError.current?<span onClick={resumeStream}>恢复</span>: null}*/}
{streamID <= 0 ? null : {streamID <= 0 ? null :
<div> <div>
{ {
me.uid === id || me.role === 'teacher' /*me.uid === id ||*/ me.role === 'teacher'
? <span className="media-btn"> ? <span className="media-btn">
{me.role === 'teacher' && me.uid !== id ?
<ArrowDownwardIcon onClick={onStageDownClick} className={style.stageDown}></ArrowDownwardIcon> <>
: null} {
<Icon onClick={onAudioClick} className={audio ? "icon-speaker-on" : "icon-speaker-off"} id != roomStore.state.course.teacherId ?<ArrowDownwardIcon onClick={onStageDownClick} className={style.stageDown}></ArrowDownwardIcon>
data={"audio"}/> :null}
<Icon onClick={onVideoClick} className={video ? "icons-camera-unmute-s" : "icons-camera-mute-s"} <Icon onClick={onAudioClick} className={audio ? "icon-speaker-on" : "icon-speaker-off"}
data={"video"}/> data={"audio"}/>
< Icon onClick = {onVideoClick} className={video ? "icons-camera-unmute-s" : "icons-camera-mute-s"}
data={"video"}/>
</>
</span> </span>
: <span className="media-btn"> : <span className="media-btn">
<Icon className={audio ? "icon-speaker-on disabled" : "icon-speaker-off disabled"} data={"audio"}/> <Icon className={audio ? "icon-speaker-on disabled" : "icon-speaker-off disabled"} data={"audio"}/>
......
...@@ -233,7 +233,7 @@ ...@@ -233,7 +233,7 @@
top: 10px; top: 10px;
pointer-events: none; pointer-events: none;
// //
top: 110px; top: 4px;
right: 10px; right: 10px;
.upload-btn { .upload-btn {
user-select: none; user-select: none;
......
...@@ -135,7 +135,7 @@ export default function Control({ ...@@ -135,7 +135,7 @@ export default function Control({
: null} : null}
</div> </div>
<div className="controls"> <div className="controls">
{/*!sharing && role === 'teacher' ? {!sharing && role === 'teacher' ?
<> <>
<ControlItem name={`first_page`} <ControlItem name={`first_page`}
active={'first_page' === current} active={'first_page' === current}
...@@ -154,7 +154,7 @@ export default function Control({ ...@@ -154,7 +154,7 @@ export default function Control({
onClick={onClick} /> onClick={onClick} />
<div className="menu-split" style={{ marginLeft: '7px', marginRight: '7px' }}></div> <div className="menu-split" style={{ marginLeft: '7px', marginRight: '7px' }}></div>
</> : null </> : null
*/} }
{role === 'teacher' ? {role === 'teacher' ?
<> <>
{/*<ControlItem*/} {/*<ControlItem*/}
......
...@@ -99,6 +99,7 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -99,6 +99,7 @@ export const RootProvider: React.FC<any> = ({children}) => {
} }
}); });
rtmClient.on("MessageFromPeer", ({ message: { text }, peerId, props }: { message: { text: string }, peerId: string, props: any }) => { rtmClient.on("MessageFromPeer", ({ message: { text }, peerId, props }: { message: { text: string }, peerId: string, props: any }) => {
console.log('MessageFromPeer', text);
const body = resolvePeerMessage(text); const body = resolvePeerMessage(text);
resolveMessage(peerId, body); resolveMessage(peerId, body);
roomStore.handlePeerMessage(body.cmd, peerId) roomStore.handlePeerMessage(body.cmd, peerId)
...@@ -114,14 +115,22 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -114,14 +115,22 @@ export const RootProvider: React.FC<any> = ({children}) => {
history.push('/'); history.push('/');
return; return;
} }
console.log('[rtm-client] updated origin attributes', JSON.parse(JSON.stringify(attributes)));
const channelAttrs = resolveChannelAttrs(attributes); const channelAttrs = resolveChannelAttrs(attributes);
// console.log('[rtm-client] updated resolved attrs', channelAttrs); // console.log('[rtm-client] updated resolved attrs', channelAttrs);
console.log('[rtm-client] updated origin attributes', attributes);
roomStore.updateRoomAttrs(channelAttrs) roomStore.updateRoomAttrs(channelAttrs)
}); });
rtmClient.on("MemberJoined", (memberId: string) => { rtmClient.on("MemberJoined", async (memberId: string) => {
console.log(memberId, '[rtm] MemberJoined MemberJoined MemberJoined MemberJoined');
if (roomStore.state.me.role === 'teacher') {
await roomStore.rtmJoinUser(memberId)
}
}); });
rtmClient.on("MemberLeft", (memberId: string) => { rtmClient.on("MemberLeft", async (memberId: string) => {
console.log(memberId, '[rtm] MemberLeft MemberLeft MemberLeft MemberLeft');
if (roomStore.state.me.role === 'teacher') {
await roomStore.rtmLeaveUser(memberId)
}
if (roomStore.state.applyUid === +memberId) { if (roomStore.state.applyUid === +memberId) {
roomStore.updateCourseLinkUid(0) roomStore.updateCourseLinkUid(0)
.then(() => { .then(() => {
...@@ -130,6 +139,7 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -130,6 +139,7 @@ export const RootProvider: React.FC<any> = ({children}) => {
} }
}); });
rtmClient.on("MemberCountUpdated", (count: number) => { rtmClient.on("MemberCountUpdated", (count: number) => {
console.log(count, '[rtm] MemberCountUpdated MemberCountUpdated MemberCountUpdated');
!ref.current && roomStore.updateMemberCount(count); !ref.current && roomStore.updateMemberCount(count);
}); });
rtmClient.on("ChannelMessage", ({ memberId, message }: { message: { text: string }, memberId: string }) => { rtmClient.on("ChannelMessage", ({ memberId, message }: { message: { text: string }, memberId: string }) => {
...@@ -151,8 +161,11 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -151,8 +161,11 @@ export const RootProvider: React.FC<any> = ({children}) => {
const location = useLocation(); const location = useLocation();
useEffect(() => { useEffect(() => {
if (location.pathname === '/') { if (location.pathname === '/') {
GlobalStorage.clear('agora_room');
return; return;
} }
const room = value.roomState; const room = value.roomState;
...@@ -171,6 +184,7 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -171,6 +184,7 @@ export const RootProvider: React.FC<any> = ({children}) => {
//@ts-ignore //@ts-ignore
window.whiteboard = whiteboardState; window.whiteboard = whiteboardState;
}, [value, location]); }, [value, location]);
return ( return (
<RootContext.Provider value={value}> <RootContext.Provider value={value}>
{children} {children}
......
...@@ -130,7 +130,7 @@ export default function useStream () { ...@@ -130,7 +130,7 @@ export default function useStream () {
if (!me.uid || userAttrs.count() === 0) return []; if (!me.uid || userAttrs.count() === 0) return [];
const teacherUid = course.teacherId; const teacherUid = course.teacherId;
const peerUsers = roomState.rtc.users; const peerUsers = roomState.rtc.users;
const studentsOrder = roomStore.state.studentsOrder || []; const studentsOrder = [...roomStore.state.studentsOrder] || [];
// exclude teacher and me // exclude teacher and me
let studentIds = peerUsers.filter((it: number) => studentsOrder.includes(it) && `${it}` !== `${teacherUid}` && `${it}` !== `${me.uid}` && `${it}` !== `${SHARE_ID}`); let studentIds = peerUsers.filter((it: number) => studentsOrder.includes(it) && `${it}` !== `${teacherUid}` && `${it}` !== `${me.uid}` && `${it}` !== `${SHARE_ID}`);
if (me.role != 'teacher') { if (me.role != 'teacher') {
...@@ -163,14 +163,23 @@ export default function useStream () { ...@@ -163,14 +163,23 @@ export default function useStream () {
const usr = userAttrs.get(`${ouid}`) const usr = userAttrs.get(`${ouid}`)
console.log('channel has ', ouid, usr) console.log('channel has ', ouid, usr)
if (usr) { if (usr) {
sids.push(-1/*+usr.uid*/); sids.push(-usr.uid/*+usr.uid*/);
} else { } else {
// sids.push(-1); // sids.push(-1);
sids[orderIdx] = 0; sids[orderIdx] = 0;
studentsOrder[orderIdx] = 0 studentsOrder[orderIdx] = 0;
/*
const _tmpStream = {
account: '空闲',
streamID: 0,
video: 0,
audio: 0,
local: false,
}
studentStreams.push(_tmpStream);
*/
} }
} }
} }
orderIdx++ orderIdx++
} }
...@@ -197,7 +206,7 @@ export default function useStream () { ...@@ -197,7 +206,7 @@ export default function useStream () {
studentStreams.push(_tmpStream); studentStreams.push(_tmpStream);
continue continue
} }
if (studentId == -1) { if (studentId < 0 ) {
const _tmpStream = { const _tmpStream = {
account: '等待中...', account: '等待中...',
streamID: studentId, streamID: studentId,
...@@ -216,7 +225,7 @@ export default function useStream () { ...@@ -216,7 +225,7 @@ export default function useStream () {
...roomState.rtc.localStream, ...roomState.rtc.localStream,
account: myAttr.account, account: myAttr.account,
video: myAttr.video, video: myAttr.video,
audio: roomState.course.muteAudio ? 0 : myAttr.audio, audio: myAttr.audio, // roomState.course.muteAudio ? 0 : myAttr.audio,
local: true, local: true,
} }
studentStreams.push(_tmpStream); studentStreams.push(_tmpStream);
...@@ -231,7 +240,7 @@ export default function useStream () { ...@@ -231,7 +240,7 @@ export default function useStream () {
streamID: studentId, streamID: studentId,
account: studentAttr.account, account: studentAttr.account,
video: studentAttr.video, video: studentAttr.video,
audio: roomState.course.muteAudio ? 0 : studentAttr.audio, audio: studentAttr.audio, // roomState.course.muteAudio ? 0 : studentAttr.audio,
} }
if (stream) { if (stream) {
_tmpStream = { _tmpStream = {
...@@ -239,10 +248,19 @@ export default function useStream () { ...@@ -239,10 +248,19 @@ export default function useStream () {
streamID: studentId, streamID: studentId,
account: studentAttr.account, account: studentAttr.account,
video: studentAttr.video, video: studentAttr.video,
audio: roomState.course.muteAudio ? 0 : studentAttr.audio, audio: studentAttr.audio , // roomState.course.muteAudio ? 0 : studentAttr.audio,
} }
} }
studentStreams.push(_tmpStream); studentStreams.push(_tmpStream);
} else {
const _tmpStream = {
account: '空闲',
streamID: 0,
video: 0,
audio: 0,
local: false,
}
studentStreams.push(_tmpStream);
} }
} }
userAttrs.forEach((v, k) => { userAttrs.forEach((v, k) => {
...@@ -252,7 +270,7 @@ export default function useStream () { ...@@ -252,7 +270,7 @@ export default function useStream () {
streamID: +v.uid, streamID: +v.uid,
account: v.account, account: v.account,
video: v.video, video: v.video,
audio: roomState.course.muteAudio ? 0 : v.audio, audio: v.audio, // roomState.course.muteAudio ? 0 : v.audio,
} }
if (`${me.uid}` == `${v.uid}`) { if (`${me.uid}` == `${v.uid}`) {
s.local = true; s.local = true;
...@@ -264,9 +282,10 @@ export default function useStream () { ...@@ -264,9 +282,10 @@ export default function useStream () {
console.log('studentStreams', studentStreams); console.log('studentStreams', studentStreams);
return studentStreams; return studentStreams;
}, [ }, [
course, // course,
me.uid, me.uid,
// roomStore.state.studentsOrder, roomStore.state.studentsOrder,
roomState.users,
roomState.rtc.users, roomState.rtc.users,
roomState.rtc.remoteStreams, roomState.rtc.remoteStreams,
roomState.rtc.localStream roomState.rtc.localStream
......
...@@ -169,10 +169,12 @@ export function RoomPage({ children }: any) { ...@@ -169,10 +169,12 @@ export function RoomPage({ children }: any) {
if (platform === 'web') { if (platform === 'web') {
// const webClient = roomStore.rtcClient as AgoraWebClient; // const webClient = roomStore.rtcClient as AgoraWebClient;
const uid = +roomStore.state.me.uid as number; const uid = +roomStore.state.me.uid as number;
const video = roomStore.state.studentsOrder.includes(uid) ? 1 : roomStore.state.me.video;
const audio = roomStore.state.studentsOrder.includes(uid) ? 1 : roomStore.state.me.audio;
const streamSpec: AgoraStreamSpec = { const streamSpec: AgoraStreamSpec = {
streamID: uid, streamID: uid,
video: true, video: !!video,
audio: true, audio: !!audio,
mirror: false, mirror: false,
screen: false, screen: false,
microphoneId: mediaDevice.microphoneId, microphoneId: mediaDevice.microphoneId,
...@@ -331,31 +333,7 @@ export function RoomPage({ children }: any) { ...@@ -331,31 +333,7 @@ export function RoomPage({ children }: any) {
return () => { return () => {
console.log('exit') console.log('exit')
const events = [ roomStore.removeRtcListener();
'onTokenPrivilegeWillExpire',
'onTokenPrivilegeDidExpire',
'error',
'stream-published',
'stream-subscribed',
'stream-added',
'stream-removed',
'peer-online',
'peer-leave',
'stream-fallback'
]
for (let eventName of events) {
webClient.rtc.off(eventName, () => {});
}
console.log("[agora-web] rtmClient logout");
roomStore.rtmClient.logout();
console.log("[agora-web] remove event listener");
!rtc.current && webClient.exit().then(() => {
console.log("[agora-web] do remove event listener");
}).catch(console.warn)
.finally(() => {
rtc.current = true;
roomStore.removeLocalStream();
});
} }
} }
/* /*
...@@ -439,17 +417,17 @@ export function RoomPage({ children }: any) { ...@@ -439,17 +417,17 @@ export function RoomPage({ children }: any) {
}, [roomState.me.uid, roomState.course.rid]); }, [roomState.me.uid, roomState.course.rid]);
useEffect(() => { useEffect(() => {
console.log('studentsOrder changed');
const webClient = roomStore.rtcClient as AgoraWebClient; const webClient = roomStore.rtcClient as AgoraWebClient;
if (!webClient.joined) { if (!webClient.joined) {
return; return;
} }
if (me.role == 'student') { if (me.role == 'student') {
if (studentsOrder.includes(+me.uid)) { if (studentsOrder.includes(+me.uid)) {
console.log(1.15,'stageup?', 1111111 ); console.log(1.15,'stageup?', me.uid, 1111111 );
roomStore.publishMeStream(); roomStore.publishMeStream();
} else { } else {
console.log(1.15,'stagedown?', 222222 ); console.log(1.15,'stagedown?', me.uid, 222222 );
roomStore.unPublishMeStream(); roomStore.unPublishMeStream();
} }
console.log(1.2 ,studentsOrder, 'studentsOrder changed publish stream'); console.log(1.2 ,studentsOrder, 'studentsOrder changed publish stream');
......
...@@ -6,7 +6,7 @@ import {List, OrderedMap, OrderedSet} from 'immutable'; ...@@ -6,7 +6,7 @@ import {List, OrderedMap, OrderedSet} from 'immutable';
import AgoraRTMClient, {RoomMessage} from '../utils/agora-rtm-client'; import AgoraRTMClient, {RoomMessage} from '../utils/agora-rtm-client';
import {globalStore} from './global'; import {globalStore} from './global';
import AgoraWebClient, {SHARE_ID} from '../utils/agora-rtc-client'; import AgoraWebClient, {SHARE_ID} from '../utils/agora-rtc-client';
import {get} from 'lodash'; import {get, isEqual} from 'lodash';
import {isElectron} from '../utils/platform'; import {isElectron} from '../utils/platform';
import GlobalStorage from '../utils/custom-storage'; import GlobalStorage from '../utils/custom-storage';
import {STAGE_NUM} from "../utils/consts"; import {STAGE_NUM} from "../utils/consts";
...@@ -62,6 +62,7 @@ export interface AgoraUser { ...@@ -62,6 +62,7 @@ export interface AgoraUser {
linkId: number // link_uid linkId: number // link_uid
lockBoard?: number // lock_board lockBoard?: number // lock_board
grantBoard: number grantBoard: number
lastStageTs: 0
} }
export interface ClassState { export interface ClassState {
...@@ -131,10 +132,18 @@ export type AgoraMediaStream = { ...@@ -131,10 +132,18 @@ export type AgoraMediaStream = {
streamID: number streamID: number
stream?: any stream?: any
} }
interface userStatus extends AgoraUser{
onStage: boolean
}
const _studentsStatus: {[key: number]: userStatus } = {};
let _sOrderStatus: number[]= [];
export class RoomStore { export class RoomStore {
private subject: Subject<RoomState> | null; private subject: Subject<RoomState> | null;
public _state: RoomState; public _state: RoomState;
private syncStudentsOrderTimer = null;
private _sOrderStatus = _sOrderStatus;
private _studentsStatus = _studentsStatus;
get state () { get state () {
return this._state; return this._state;
...@@ -245,8 +254,8 @@ export class RoomStore { ...@@ -245,8 +254,8 @@ export class RoomStore {
console.log("[agora-web] dual stream set low for student"); console.log("[agora-web] dual stream set low for student");
} }
} }
console.log('stream', 3, 'addRemoteStream to play') console.log('stream step:', 3,'uid', streamID, 'addRemoteStream to play')
const _stream = new AgoraStream(stream, stream.getId(), false); const _stream = new AgoraStream(stream, streamID, false);
console.log("[agora-web] subscribe remote stream, id: ", stream.getId()); console.log("[agora-web] subscribe remote stream, id: ", stream.getId());
roomStore.addRemoteStream(_stream); roomStore.addRemoteStream(_stream);
}); });
...@@ -295,36 +304,57 @@ export class RoomStore { ...@@ -295,36 +304,57 @@ export class RoomStore {
// debugger // debugger
//把台上位置变零 //把台上位置变零
let studentsOrder: any[] = [...this.state.studentsOrder]; let studentsOrder: any[] = [...this.state.studentsOrder];
console.log('下台', uid, studentsOrder)
const uidIndex = studentsOrder.indexOf(+uid) const uidIndex = studentsOrder.indexOf(+uid)
if (uidIndex > -1) { if (uidIndex > -1) {
studentsOrder.splice(uidIndex, 1, 0); studentsOrder.splice(uidIndex, 1, 0);
console.log('下台后', uid, studentsOrder)
const data: any = {}; const data: any = {};
data['studentsOrder'] = studentsOrder; data['studentsOrder'] = studentsOrder;
const downUser: AgoraUser | undefined = this.state.users.get(`${uid}`); const downUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
if (downUser) { if (downUser) {
downUser.audio = 0; downUser.audio = 0;
downUser.video = 0; downUser.video = 0;
// @ts-ignore
downUser.last_stage_ts = 0;
data[uid] = downUser data[uid] = downUser
} }
this.state = {
...this.state,
studentsOrder
}
await this.commit(this.state);
await this.rtmClient.addOrUpdateChannelAttributes(data); await this.rtmClient.addOrUpdateChannelAttributes(data);
} }
} }
async stageUp(uid: string) { async stageUp(uid: string) {
let studentsOrder: any[] = [...this.state.studentsOrder]; let studentsOrder: any[] = [...this.state.studentsOrder];
if (studentsOrder.indexOf(+uid) > -1) {
return;
}
const data: any = {}; const data: any = {};
const emptyIndex = studentsOrder.indexOf(0); const emptyIndex = studentsOrder.indexOf(0);
if (emptyIndex > -1) { if (emptyIndex > -1 && !studentsOrder.includes(uid)) {
studentsOrder[emptyIndex] = +uid; studentsOrder[emptyIndex] = +uid;
const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`); const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
if (upUser) { if (upUser) {
upUser.video = 1; upUser.video = 1;
upUser.audio = 1; upUser.audio = 1;
// @ts-ignore
upUser.last_stage_ts = Date.now();
data[upUser.uid] = upUser; data[upUser.uid] = upUser;
} }
} else { } else {
return return
} }
this.state = {
...this.state,
studentsOrder
}
await this.commit(this.state);
data['studentsOrder'] = studentsOrder; data['studentsOrder'] = studentsOrder;
await this.rtmClient.addOrUpdateChannelAttributes(data); await this.rtmClient.addOrUpdateChannelAttributes(data);
} }
...@@ -451,8 +481,10 @@ export class RoomStore { ...@@ -451,8 +481,10 @@ export class RoomStore {
this.subject = null; this.subject = null;
} }
commit (state: RoomState) { async commit (state: RoomState) {
this.subject && this.subject.next(state); console.log('state changed', state);
return this.subject && this.subject.next(state);
} }
updateState(rootState: RoomState) { updateState(rootState: RoomState) {
...@@ -479,7 +511,7 @@ export class RoomStore { ...@@ -479,7 +511,7 @@ export class RoomStore {
return false; return false;
} }
addLocalStream(stream: AgoraStream) { async addLocalStream(stream: AgoraStream) {
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -487,10 +519,10 @@ export class RoomStore { ...@@ -487,10 +519,10 @@ export class RoomStore {
localStream: stream localStream: stream
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
removeLocalStream() { async removeLocalStream() {
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -499,10 +531,10 @@ export class RoomStore { ...@@ -499,10 +531,10 @@ export class RoomStore {
localSharedStream: null localSharedStream: null
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
addLocalSharedStream(stream: any) { async addLocalSharedStream(stream: any) {
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -510,10 +542,10 @@ export class RoomStore { ...@@ -510,10 +542,10 @@ export class RoomStore {
localSharedStream: stream localSharedStream: stream
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
removeLocalSharedStream() { async removeLocalSharedStream() {
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -521,10 +553,13 @@ export class RoomStore { ...@@ -521,10 +553,13 @@ export class RoomStore {
localSharedStream: null localSharedStream: null
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
addPeerUser(uid: number) { async addPeerUser(uid: number) {
if (!uid) {
return;
}
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -532,21 +567,45 @@ export class RoomStore { ...@@ -532,21 +567,45 @@ export class RoomStore {
users: this.state.rtc.users.add(uid), users: this.state.rtc.users.add(uid),
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
removePeerUser(uid: number) { async removePeerUser(uid: number) {
// 如果一个流退了,检测是否是台上人员,移除掉,其实就是下台
// await this.stageDown(`${uid}`)
const orders = [...this.state.studentsOrder];
const checkIdx1 = orders.indexOf(+uid)
const checkIdx2 = orders.indexOf(-uid)
if (checkIdx1 > 0) {
orders.splice(checkIdx1, 1, 0)
}
if (checkIdx2 > 0) {
orders.splice(checkIdx2, 1, 0)
}
const remoteStream = this.state.rtc.remoteStreams.get(uid);
if (platform === 'web') {
if (remoteStream && remoteStream.stream && remoteStream.stream.isPlaying) {
remoteStream.stream.isPlaying() && remoteStream.stream.stop();
}
}
if (checkIdx1 > 0 || checkIdx2 > 0) {
await this.rtmClient.addOrUpdateChannelAttributes({studentsOrder: orders});
}
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...this.state.rtc, ...this.state.rtc,
users: this.state.rtc.users.delete(uid), users: this.state.rtc.users.delete(uid),
} },
studentsOrder: orders
} }
this.commit(this.state); await this.commit(this.state);
} }
addRemoteStream(stream: AgoraStream) { async addRemoteStream(stream: AgoraStream) {
console.log('remoteStreams added',5 , stream.streamID, this.state.users) console.log('remoteStreams added',5 , stream.streamID, this.state.users)
this.state = { this.state = {
...this.state, ...this.state,
...@@ -555,16 +614,11 @@ export class RoomStore { ...@@ -555,16 +614,11 @@ export class RoomStore {
remoteStreams: this.state.rtc.remoteStreams.set(stream.streamID, stream) remoteStreams: this.state.rtc.remoteStreams.set(stream.streamID, stream)
} }
} }
this.commit(this.state); await this.commit(this.state);
} }
removeRemoteStream(uid: number) { async removeRemoteStream(uid: number) {
const remoteStream = this.state.rtc.remoteStreams.get(uid);
if (platform === 'web') {
if (remoteStream && remoteStream.stream && remoteStream.stream.isPlaying) {
remoteStream.stream.isPlaying() && remoteStream.stream.stop();
}
}
this.state = { this.state = {
...@@ -572,11 +626,14 @@ export class RoomStore { ...@@ -572,11 +626,14 @@ export class RoomStore {
rtc: { rtc: {
...this.state.rtc, ...this.state.rtc,
remoteStreams: this.state.rtc.remoteStreams.delete(uid) remoteStreams: this.state.rtc.remoteStreams.delete(uid)
} },
} }
await this.commit(this.state);
} }
updateMemberCount(count: number) { async updateMemberCount(count: number) {
console.log('updateMemberCount1', this.state.studentsOrder);
this.state = { this.state = {
...this.state, ...this.state,
rtm: { rtm: {
...@@ -584,7 +641,8 @@ export class RoomStore { ...@@ -584,7 +641,8 @@ export class RoomStore {
memberCount: count, memberCount: count,
} }
} }
this.commit(this.state); // await this.commit(this.state);
console.log('updateMemberCount2', this.state.studentsOrder);
} }
updateRtc(newState: any) { updateRtc(newState: any) {
...@@ -609,7 +667,7 @@ export class RoomStore { ...@@ -609,7 +667,7 @@ export class RoomStore {
async handlePeerMessage(cmd: RoomMessage, peerId: string) { async handlePeerMessage(cmd: RoomMessage, peerId: string) {
if (!peerId) return console.warn('state is not assigned'); if (!peerId) return console.warn('state is not assigned');
const myUid = this.state.me.uid; const myUid = this.state.me.uid;
console.log("Teacher: ", this.isTeacher(myUid) , ", peerId: ", this.isStudent(peerId)); console.log("Teacher: ", this.isTeacher(myUid) , ", peerId: ", this.isStudent(peerId), cmd);
// student follow teacher peer message // student follow teacher peer message
if (!this.isTeacher(myUid) && this.isTeacher(peerId)) { if (!this.isTeacher(myUid) && this.isTeacher(peerId)) {
...@@ -676,6 +734,14 @@ export class RoomStore { ...@@ -676,6 +734,14 @@ export class RoomStore {
// when i m teacher & received student message // when i m teacher & received student message
if (this.isTeacher(myUid) && this.isStudent(peerId)) { if (this.isTeacher(myUid) && this.isStudent(peerId)) {
switch(cmd) { switch(cmd) {
case RoomMessage.stageUp: {
this.stageUp(peerId)
return
}
case RoomMessage.stageDown: {
this.stageDown(peerId);
return
}
case RoomMessage.applyCoVideo: { case RoomMessage.applyCoVideo: {
// WARN: LOCK // WARN: LOCK
if (this.state.course.linkId) { if (this.state.course.linkId) {
...@@ -758,6 +824,17 @@ export class RoomStore { ...@@ -758,6 +824,17 @@ export class RoomStore {
} }
async unmute(uid: string, type: string) { async unmute(uid: string, type: string) {
// 有一个unmute的时候,就把course里的muteAudio 变成0
if (this.state.me.role == 'teacher') {
this.state = {
...this.state,
course: {
...this.state.course,
muteAudio: 0
}
}
this.commit(this.state)
}
const me = this.state.me; const me = this.state.me;
if (me.uid === `${uid}`) { if (me.uid === `${uid}`) {
if (type === 'audio') { if (type === 'audio') {
...@@ -806,6 +883,7 @@ export class RoomStore { ...@@ -806,6 +883,7 @@ export class RoomStore {
const channelMemberCount = await this.rtmClient.getChannelMemberCount([rid]); const channelMemberCount = await this.rtmClient.getChannelMemberCount([rid]);
const channelCount = channelMemberCount[rid]; const channelCount = channelMemberCount[rid];
const channelMeta = await this.rtmClient.getChannelAttributeBy(rid); const channelMeta = await this.rtmClient.getChannelAttributeBy(rid);
console.log('channelMeta', channelMeta)
if (payload.role == 'student' && !channelMeta.teacher) { if (payload.role == 'student' && !channelMeta.teacher) {
throw { throw {
type: 'not_permitted', type: 'not_permitted',
...@@ -837,8 +915,8 @@ export class RoomStore { ...@@ -837,8 +915,8 @@ export class RoomStore {
payload.audio = 0; payload.audio = 0;
payload.video = 0; payload.video = 0;
if (role === 'teacher') { if (role === 'teacher') {
payload.audio = 1; payload.audio = channelMeta['teacher'] ? channelMeta['teacher'].audio : 1;
payload.video = 1; payload.video = channelMeta['teacher'] ? channelMeta['teacher'].video : 1;
} }
const argsJoin = { const argsJoin = {
...@@ -862,56 +940,25 @@ export class RoomStore { ...@@ -862,56 +940,25 @@ export class RoomStore {
} }
} else { } else {
//查看有无空闲 //查看有无空闲
if (!studentsOrder.includes(+payload.uid)) { /*if (!studentsOrder.includes(+payload.uid)) {
const emptyIndex = studentsOrder.indexOf(0); const emptyIndex = studentsOrder.indexOf(0);
if (emptyIndex > -1) { if (emptyIndex > -1) {
studentsOrder.splice(emptyIndex, 1, +payload.uid); studentsOrder.splice(emptyIndex, 1, +payload.uid);
} }
} }
/*
if (!studentsOrder.length) {
accounts.forEach(a => {
if (!studentsOrder.includes(+a.uid)) {
studentsOrder.push(+a.uid);
}
})
} else {
const accs = accounts.filter(a=> a.role != 'teacher');
const accIds = accs.map(a=> +a.uid);
// if (accIds.length >= studentsOrder.length) {
let a = new Set(accIds);
let b = new Set(studentsOrder);
let diff = new Set([...a].filter(x => !b.has(x)));
diff.forEach(d => {
if (!studentsOrder.includes(+d)) {
studentsOrder.push(+d);
}
})
// }
}
if (!studentsOrder.includes(+uid)) {
studentsOrder.push(+uid); */
} else {
if (!pass) {
const i = studentsOrder.indexOf(+uid)
studentsOrder.splice(i,1)
studentsOrder.push(+uid)
}
} }
*/ console.log('login accounts', accounts)
let onlineMe = accounts.find((it: any) => `${it.uid}` == `${payload.uid}`);
console.log('login accounts', accounts) if (onlineMe) {
let onlineMe = accounts.find((it: any) => `${it.uid}` == `${payload.uid}`); payload.video = onlineMe.video;
if (onlineMe) { payload.audio = onlineMe.audio;
payload.video = onlineMe.video; } else {
payload.audio = onlineMe.audio; if (studentsOrder && studentsOrder.includes(+payload.uid)) {
} else { payload.video = 1;
if (studentsOrder && studentsOrder.includes(+payload.uid)) { payload.audio = 1;
payload.video = 1;
payload.audio = 1;
}
} }
} }
console.log('login payload', payload) console.log('login payload', payload)
...@@ -926,7 +973,7 @@ export class RoomStore { ...@@ -926,7 +973,7 @@ export class RoomStore {
} }
} }
const grantBoard = role === 'teacher' ? 1 : 0; const grantBoard = role === 'teacher' ? 1 : 0;
console.log(">>>>>>>>>>#room: ", grantBoard); console.log(">>>>>>>>>>#room: ", grantBoard,this.state);
await this.updateMe({...payload, grantBoard}, studentsOrder); await this.updateMe({...payload, grantBoard}, studentsOrder);
this.commit(this.state); this.commit(this.state);
}); });
...@@ -950,7 +997,7 @@ export class RoomStore { ...@@ -950,7 +997,7 @@ export class RoomStore {
} }
} }
publishMeStream() { publishMeStream() {
console.log(3, 'set rtc join flag, should auto publish') console.log(3, 'set rtc join flag, should auto publish', this.state.rtc.joined)
// return await (this.rtcClient as AgoraWebClient).publishStream() // return await (this.rtcClient as AgoraWebClient).publishStream()
// 通过index.tsx里的监听来触发发布 // 通过index.tsx里的监听来触发发布
if (!this.state.rtc.joined) { if (!this.state.rtc.joined) {
...@@ -959,7 +1006,7 @@ export class RoomStore { ...@@ -959,7 +1006,7 @@ export class RoomStore {
} }
unPublishMeStream() { unPublishMeStream() {
console.log(3, 'set rtc join flag, should auto publish') console.log(3, 'set rtc join flag false, should unPublishMeStream')
// return await (this.rtcClient as AgoraWebClient).publishStream() // return await (this.rtcClient as AgoraWebClient).publishStream()
if (this.state.rtc.joined) { if (this.state.rtc.joined) {
this.setRTCJoined(false); this.setRTCJoined(false);
...@@ -967,6 +1014,7 @@ export class RoomStore { ...@@ -967,6 +1014,7 @@ export class RoomStore {
} }
setRTCJoined(joined: boolean) { setRTCJoined(joined: boolean) {
console.log('setRTCJoined', joined);
this.state = { this.state = {
...this.state, ...this.state,
rtc: { rtc: {
...@@ -976,6 +1024,20 @@ export class RoomStore { ...@@ -976,6 +1024,20 @@ export class RoomStore {
} }
this.commit(this.state); this.commit(this.state);
} }
async leaveStage(uid: number) {
const sOrders = [...this.state.studentsOrder];
const idx = sOrders.indexOf(+uid)
const newInfo: any = {};
if (idx > -1) {
sOrders.splice(idx, 1, 0);
this.state = {
...this.state,
studentsOrder: sOrders
}
newInfo['studentsOrder'] = sOrders;
await this.rtmClient.addOrUpdateChannelAttributes(newInfo);
}
}
async updateCourseLinkUid(linkId: number) { async updateCourseLinkUid(linkId: number) {
const me = this.state.me; const me = this.state.me;
...@@ -1024,6 +1086,7 @@ export class RoomStore { ...@@ -1024,6 +1086,7 @@ export class RoomStore {
audio: user.audio, audio: user.audio,
chat: user.chat, chat: user.chat,
grant_board: user.grantBoard, grant_board: user.grantBoard,
last_stage_ts: user.lastStageTs
} }
if (user.role === 'teacher') { if (user.role === 'teacher') {
Object.assign(attrs, { Object.assign(attrs, {
...@@ -1047,27 +1110,47 @@ export class RoomStore { ...@@ -1047,27 +1110,47 @@ export class RoomStore {
} }
async allStudentMuteOn(){ async allStudentMuteOn(){
const channelMeta = await this.rtmClient.getChannelAttributeBy(this.state.course.rid); const channelMeta = await this.rtmClient.getChannelAttributeBy(this.state.course.rid);
// let accounts = channelMeta.accounts; let accounts = channelMeta.accounts;
const data: any = {}; const data: any = {};
// accounts.forEach((acc: any) => { accounts.forEach((acc: any) => {
// acc.audio = 0; acc.audio = 0;
// data[acc.uid] = acc; data[acc.uid] = acc;
// }) })
channelMeta.teacher.mute_audio = 0; // channelMeta.teacher.mute_audio = 0;
data['teacher'] = channelMeta.teacher; // data['teacher'] = channelMeta.teacher;
await this.rtmClient.addOrUpdateChannelAttributes(data); this.state = {
...this.state,
course: {
...this.state.course,
muteAudio: 1
},
}
this.commit(this.state);
if (Object.keys(data).length) {
await this.rtmClient.addOrUpdateChannelAttributes(data);
}
} }
async allStudentMuteOff(){ async allStudentMuteOff(){
const channelMeta = await this.rtmClient.getChannelAttributeBy(this.state.course.rid); const channelMeta = await this.rtmClient.getChannelAttributeBy(this.state.course.rid);
// let accounts = channelMeta.accounts; let accounts = channelMeta.accounts;
const data: any = {}; const data: any = {};
// accounts.forEach((acc: any) => { accounts.forEach((acc: any) => {
// acc.audio = 1; acc.audio = 1;
// data[acc.uid] = acc; data[acc.uid] = acc;
// }) })
channelMeta.teacher.mute_audio = 1; // channelMeta.teacher.mute_audio = 1;
data['teacher'] = channelMeta.teacher; // data['teacher'] = channelMeta.teacher;
await this.rtmClient.addOrUpdateChannelAttributes(data); this.state = {
...this.state,
course: {
...this.state.course,
muteAudio: 0
},
}
this.commit(this.state);
if (Object.keys(data).length) {
await this.rtmClient.addOrUpdateChannelAttributes(data);
}
} }
async updateMe(user: any, studentsOrder: number[] = []) { async updateMe(user: any, studentsOrder: number[] = []) {
...@@ -1091,6 +1174,7 @@ export class RoomStore { ...@@ -1091,6 +1174,7 @@ export class RoomStore {
link_uid: linkId, link_uid: linkId,
shared_uid: sharedId, shared_uid: sharedId,
grant_board: grantBoard, grant_board: grantBoard,
last_stage_ts: Date.now()
}); });
console.log('users', user); console.log('users', user);
...@@ -1103,7 +1187,7 @@ export class RoomStore { ...@@ -1103,7 +1187,7 @@ export class RoomStore {
const class_state = get(user, 'courseState', this.state.course.courseState); const class_state = get(user, 'courseState', this.state.course.courseState);
const whiteboard_uid = get(user, 'boardId', this.state.course.boardId); const whiteboard_uid = get(user, 'boardId', this.state.course.boardId);
const mute_chat = get(user, 'muteChat', this.state.course.muteChat); const mute_chat = get(user, 'muteChat', this.state.course.muteChat);
const mute_audio = get(user, 'muteChat', this.state.course.muteAudio); const mute_audio = get(user, 'muteAudio', this.state.course.muteAudio);
const shared_uid = get(user, 'sharedId', this.state.course.sharedId); const shared_uid = get(user, 'sharedId', this.state.course.sharedId);
const link_uid = get(user, 'linkId', this.state.course.linkId); const link_uid = get(user, 'linkId', this.state.course.linkId);
const lock_board = get(user, 'lockBoard', this.state.course.lockBoard); const lock_board = get(user, 'lockBoard', this.state.course.lockBoard);
...@@ -1131,204 +1215,200 @@ export class RoomStore { ...@@ -1131,204 +1215,200 @@ export class RoomStore {
// let res = await this.rtmClient.updateChannelAttrsByKey(key, attrs); // let res = await this.rtmClient.updateChannelAttrsByKey(key, attrs);
// return res; // return res;
} }
async rtmLeaveUser(memberId: string) {
async syncStageStudent__(accounts: any[], studentsOrder: number[]) { console.log('学生退出', memberId, this.state.studentsOrder);
// debugger const studentsOrder = [...this.state.studentsOrder];
let needUpdate =false; const userIndex = studentsOrder.indexOf(+memberId);
const data: any = {}; if (userIndex > -1) {
const accData: any = {}; studentsOrder.splice(userIndex, 1, 0)
accounts.forEach(acc => { this.state = {
if (acc.role == 'student') { ...this.state,
accData[acc.uid] = acc studentsOrder
} }
}) await this.commit(this.state);
// const data: any = {};
// data['studentsOrder'] = studentsOrder;
const accIds: any[] = Object.keys(accData).map(uid => +uid); // console.log('update me', data);
if (!accIds.length) { // await this.rtmClient.addOrUpdateChannelAttributes(data);
return await this.rtmClient.clearChannelAttributesByKeys([`${memberId}`]);
} }
// 学生在台上并且退出了
const exitUserIds = studentsOrder.filter( uid => {
return !accIds.includes(uid) && uid != 0;
});
if (exitUserIds.length) {
exitUserIds.forEach( uid => {
if (studentsOrder.includes(uid)) {
studentsOrder.splice(studentsOrder.indexOf(uid), 1)
needUpdate = true
}
})
} }
async rtmJoinUser(memberId: string) {
console.log('新学生登录', memberId, this.state.studentsOrder);
const data: any = {};
let needUpdate = false;
const newUid = +memberId;
const studentsOrder = [...this.state.studentsOrder];
if (studentsOrder.length != STAGE_NUM) { if (studentsOrder.length != STAGE_NUM) {
if (studentsOrder.length > STAGE_NUM) { const len = STAGE_NUM - studentsOrder.length ? STAGE_NUM - studentsOrder.length : 0;
//台上队列尾部挤下去的人音视频关闭 Array(len).forEach(_ =>{
const lostIdx: number[] = []; studentsOrder.push(0)
[...Array(studentsOrder.length - STAGE_NUM)].forEach( (_, i) => { })
console.log(i)
const idx = studentsOrder.length - 1 - i
lostIdx.push(idx)
})
lostIdx.forEach( uid => {
if (studentsOrder.includes(uid)) {
if ( accData[uid]) {
const u = accData[uid];
// if (u.video != 0 || u.audio != 0) {
u.video = 0;
u.audio = 0;
data[uid] = u;
// }
}
studentsOrder.splice(studentsOrder.indexOf(uid), 1)
needUpdate = true
}
})
studentsOrder.length = STAGE_NUM;
}
if (studentsOrder.length < STAGE_NUM) {
const newUid = accIds.find( uid => {
return !studentsOrder.includes(+uid) ;
});
const newStudents = accData[newUid];
if (newStudents) {
const uid = newStudents.uid;
// if (newStudents.video != 1 || newStudents.audio != 1) {
//上台的打开音视频
newStudents.video = 1;
newStudents.audio = 1;
data[uid] = newStudents;
studentsOrder.push(+uid)
needUpdate = true;
// }
}
}
} }
for (const uid of Object.keys(accData)) { if (studentsOrder.includes(newUid)) {
if (!studentsOrder.includes(+uid)) { return;
const ud = {...accData[uid]}; } else {
ud.video = 0; // 台上有空闲
ud.audio = 0; const emptyIndex = studentsOrder.indexOf(0);
data[uid] = ud if (emptyIndex > -1) {
//从台下找人
studentsOrder.splice(emptyIndex, 1, +newUid)
console.log('加入studentsOrder', newUid, studentsOrder);
needUpdate = true;
} }
} }
if (needUpdate) { if (needUpdate) {
data['studentsOrder'] = studentsOrder; this.state = {
// await this.rtmClient.addOrUpdateChannelAttributes(data); ...this.state,
return data; studentsOrder
}
// this.state.studentsOrder = studentsOrder;
await this.commit(this.state);
} }
console.log('加入完登录学生后', this.state.studentsOrder);
} }
async syncStageStudent(accounts: any[], studentsOrder: number[]) {
// debugger async syncStageStudentByTeacherOrder(accounts: any[], channelStudentsOrder: number[]){
let needUpdate =false; console.log('pass in channel studentsOrder', channelStudentsOrder);
const data: any = {}; console.log('teacher keep studentsOrder', this.state.studentsOrder);
const tSOrder = [...this.state.studentsOrder]
const accData: any = {}; const accData: any = {};
const data: any = {};
accounts.forEach(acc => { accounts.forEach(acc => {
if (acc.role == 'student') { if (acc.role == 'student') {
console.log('account', acc.uid, acc)
accData[acc.uid] = acc accData[acc.uid] = acc
} }
})
const accIds: any[] = Object.keys(accData).map(uid => +uid);
if (!accIds.length) {
return
}
// 学生在台上并且退出了
const exitUserIds = studentsOrder.filter( uid => {
return !accIds.includes(uid) && uid != 0;
}); });
if (exitUserIds.length) {
exitUserIds.forEach( uid => {
if (studentsOrder.includes(uid)) {
studentsOrder.splice(studentsOrder.indexOf(uid), 1, 0)
needUpdate = true
}
})
} let shouldOut = channelStudentsOrder.filter(x => !tSOrder.includes(x) && x !== 0);
if (studentsOrder.length != STAGE_NUM) { let shouldIn = tSOrder.filter(x => !channelStudentsOrder.includes(x) && x !== 0);
if (studentsOrder.length > STAGE_NUM) { console.log('shouldOut', shouldOut);
//台上队列尾部挤下去的人音视频关闭 shouldOut.forEach(o => {
const lostIdx: number[] = []; const oi = tSOrder.indexOf(o);
[...Array(studentsOrder.length - STAGE_NUM)].forEach( (_, i) => { if (oi > -1) {
console.log(i) tSOrder.splice(oi, 1, 0)
const idx = studentsOrder.length - 1 - i
lostIdx.push(idx)
})
lostIdx.forEach( uid => {
if (studentsOrder.includes(uid)) {
if ( accData[uid]) {
const u = accData[uid];
// if (u.video != 0 || u.audio != 0) {
u.video = 0;
u.audio = 0;
data[uid] = u;
// }
}
studentsOrder.splice(studentsOrder.indexOf(uid), 1)
needUpdate = true
}
})
studentsOrder.length = STAGE_NUM;
} }
if (studentsOrder.length < STAGE_NUM) { })
const newUid = accIds.find( uid => { console.log('after shouldOut', tSOrder);
return !studentsOrder.includes(+uid) ; console.log('shouldIn', shouldIn);
});
const newStudents = accData[newUid]; shouldIn.forEach(o => {
if (newStudents) { const user = accData[o];
const uid = newStudents.uid; if (user) {
// if (newStudents.video != 1 || newStudents.audio != 1) { user.audio = 1;
//上台的打开音视频 user.video = 1;
newStudents.video = 1; data[o] = user;
newStudents.audio = 1;
data[uid] = newStudents;
studentsOrder.push(+uid)
needUpdate = true;
// }
}
} }
} else { if(!tSOrder.includes(o)) {
// 台上有空闲 const ii = tSOrder.indexOf(0);
const emptyIndex = studentsOrder.indexOf(0); if (ii > -1) {
if (emptyIndex > -1) { tSOrder.splice(ii, 1, o)
//和小于台上人数补进去一样
const newUid = accIds.find( uid => {
return !studentsOrder.includes(+uid) ;
});
studentsOrder.splice(emptyIndex, 1, +newUid)
const newStudents = accData[newUid];
if (newStudents) {
const uid = newStudents.uid;
// if (newStudents.video != 1 || newStudents.audio != 1) {
//上台的打开音视频
newStudents.video = 1;
newStudents.audio = 1;
data[uid] = newStudents;
studentsOrder.push(+uid)
needUpdate = true;
// }
} }
} }
})
console.log('after shouldIn', tSOrder);
if (shouldIn.length || shouldOut.length) {
data['studentsOrder'] = [...tSOrder];
return data
}
return
//不在直播间的学生过滤出来
const exitUsers = tSOrder.filter(uid => {
return !accData[uid];
})
let localSync = false;
console.log('不在直播间的学生',exitUsers, tSOrder);
exitUsers.forEach(uid => {
const idx1 = channelStudentsOrder.indexOf(+uid);
if (idx1 > -1){
channelStudentsOrder.splice(idx1, 1 ,0)
}
const idx2 = tSOrder.indexOf(+uid);
if (idx2 > -1){
localSync = true;
tSOrder.splice(idx2, 1 ,0)
}
})
console.log('移除不在直播间的学生后', channelStudentsOrder, tSOrder);
if (tSOrder.length < STAGE_NUM) {
Array(STAGE_NUM - tSOrder.length).fill(0).forEach((zero) => {
tSOrder.push(zero)
})
} }
for (const uid of Object.keys(accData)) {
if (!studentsOrder.includes(+uid)) {
const ud = {...accData[uid]}; let shouldDown = channelStudentsOrder.filter(x => !tSOrder.includes(x) && x !== 0);
ud.video = 0; let shouldUp = tSOrder.filter(x => !channelStudentsOrder.includes(x) && x !== 0);
ud.audio = 1; console.log('shouldDown', shouldDown);
data[uid] = ud
console.log('shouldUp', shouldUp);
shouldDown.forEach(d => {
const user = accData[d];
if (user) {
user.audio = 0;
user.video = 0;
data[d] = user;
} }
})
shouldUp.forEach(u => {
const user = accData[u];
if (user) {
user.audio = 1;
user.video = 1;
data[u] = user;
}
})
// studentsOrder.forEach(uid => {
// if (uid != 0) {
// const user = accData[uid];
// if (!user.audio ) {
// this.rtmClient.sendPeerMessage(`${uid}`, {cmd: RoomMessage.unmuteAudio});
// }
// if (!user.video) {
// this.rtmClient.sendPeerMessage(`${uid}`, {cmd: RoomMessage.unmuteVideo});
// }
// }
// })
if (localSync){
console.log('同步教师sorders', tSOrder);
this.state = {
...this.state,
studentsOrder: tSOrder
}
await this.commit(this.state)
} }
if (needUpdate) {
data['studentsOrder'] = studentsOrder; if (Object.keys(data).length) {
// await this.rtmClient.addOrUpdateChannelAttributes(data); data['studentsOrder'] = [...tSOrder];
return data; return data
} }
} }
async updateRoomAttrs ({teacher, accounts, room, studentsOrder}: {teacher: any, accounts: any, room: any, studentsOrder: number[]}) { async updateRoomAttrs ({teacher, accounts, room, studentsOrder}: {teacher: any, accounts: any, room: any, studentsOrder: number[]}) {
console.log("[agora-board], room: ",teacher, accounts, room, studentsOrder); console.log("[agora-board], room: ",teacher, accounts, room, studentsOrder);
const exitUserIds = []; const exitUserIds = [];
const users: OrderedMap<string, AgoraUser> = accounts.reduce((acc: OrderedMap<string, AgoraUser>, it: any) => { const users: OrderedMap<string, AgoraUser> = accounts.reduce((acc: OrderedMap<string, AgoraUser>, it: any) => {
return acc.set(it.uid, { return acc.set(it.uid, {
...@@ -1343,9 +1423,12 @@ export class RoomStore { ...@@ -1343,9 +1423,12 @@ export class RoomStore {
linkId: it.link_uid, linkId: it.link_uid,
lockBoard: it.lock_board, lockBoard: it.lock_board,
grantBoard: it.grant_board, grantBoard: it.grant_board,
lastStageTs: it.last_stage_ts
}); });
}, OrderedMap<string, AgoraUser>()); }, OrderedMap<string, AgoraUser>());
if (this.state.studentsOrder.length == 0) {
this.state.studentsOrder = [0,0,0,0,0,0]
}
/* /*
let users = OrderedMap<string, AgoraUser>(); let users = OrderedMap<string, AgoraUser>();
for (const it of accounts) { for (const it of accounts) {
...@@ -1365,13 +1448,7 @@ export class RoomStore { ...@@ -1365,13 +1448,7 @@ export class RoomStore {
} }
*/ */
console.log('users ', users.toArray()); console.log('users ', users.toArray());
// remove exist user from studentsOrder
// for (const uid of [...studentsOrder]) {
// const has = users.get(`${uid}`)
// if (!has) {
// studentsOrder.splice(studentsOrder.indexOf(+uid), 1)
// }
// }
const me = this.state.me; const me = this.state.me;
...@@ -1387,21 +1464,49 @@ export class RoomStore { ...@@ -1387,21 +1464,49 @@ export class RoomStore {
// this.meDown() // this.meDown()
// } // }
// } // }
let newStudentsOrder = studentsOrder; let newStudentsOrder = studentsOrder;
console.log('频道StudentsOrder', newStudentsOrder);
if (me.role === 'teacher') { if (me.role === 'teacher') {
/*
// remove exist user from studentsOrder
for (const uid of [...studentsOrder]) {
if (uid <= 0) {
continue
}
const has = users.get(`${uid}`)
if (!has) {
studentsOrder.splice(studentsOrder.indexOf(+uid), 1)
}
}*/
newStudentsOrder = [...this.state.studentsOrder];//studentsOrder;
Object.assign(me, { Object.assign(me, {
linkId: room.link_uid, linkId: room.link_uid,
boardId: room.whiteboard_uid, boardId: room.whiteboard_uid,
lockBoard: room.lock_board, lockBoard: room.lock_board,
}) })
if (this.syncStudentsOrderTimer) {
// @ts-ignore
clearTimeout(this.syncStudentsOrderTimer);
this.syncStudentsOrderTimer = null;
}
//教师维护学生顺序,仅保留STAGE_NUM个,并更新到频道属性 //教师维护学生顺序,仅保留STAGE_NUM个,并更新到频道属性
/*const newInfo: any = await this.syncStageStudent(JSON.parse(JSON.stringify(accounts)), [...studentsOrder]); const newInfo: any = await this.syncStageStudentByTeacherOrder(JSON.parse(JSON.stringify(accounts)), [...studentsOrder/*this.state.studentsOrder*/]);
console.log('newInfo', newInfo) console.log('newInfo', newInfo)
// debugger // debugger
if (newInfo) { if (newInfo) {
newStudentsOrder = newInfo['studentsOrder']; newStudentsOrder = newInfo['studentsOrder'];
await this.rtmClient.addOrUpdateChannelAttributes(newInfo); await this.rtmClient.addOrUpdateChannelAttributes(newInfo);
}*/ // @ts-ignore
// this.syncStudentsOrderTimer = setTimeout(async () => {
// }, 1000)
} else {
// newStudentsOrder = [];
}
} }
const newClassState: ClassState = {} as ClassState; const newClassState: ClassState = {} as ClassState;
...@@ -1411,7 +1516,7 @@ export class RoomStore { ...@@ -1411,7 +1516,7 @@ export class RoomStore {
boardId: room.whiteboard_uid, boardId: room.whiteboard_uid,
courseState: room.class_state, courseState: room.class_state,
muteChat: room.mute_chat, muteChat: room.mute_chat,
muteAudio: room.mute_audio, // muteAudio: room.mute_audio,
lockBoard: room.lock_board, lockBoard: room.lock_board,
currentTool: room.current_tool currentTool: room.current_tool
}) })
...@@ -1432,6 +1537,9 @@ export class RoomStore { ...@@ -1432,6 +1537,9 @@ export class RoomStore {
}, },
studentsOrder: newStudentsOrder studentsOrder: newStudentsOrder
} }
// if (newStudentsOrder.length) {
// this.state.studentsOrder = newStudentsOrder;
// }
this.commit(this.state); this.commit(this.state);
} }
...@@ -1459,7 +1567,44 @@ export class RoomStore { ...@@ -1459,7 +1567,44 @@ export class RoomStore {
} }
this.commit(this.state); this.commit(this.state);
} }
async removeRtcListener() {
const iframes = document.querySelectorAll("iframe");
if (iframes && iframes.length) {
iframes.forEach(ifm => {
// @ts-ignore
ifm.contentWindow.location.href='about:blank'
// @ts-ignore
ifm.contentWindow.document.write('')
})
}
const events = [
'onTokenPrivilegeWillExpire',
'onTokenPrivilegeDidExpire',
'error',
'stream-published',
'stream-subscribed',
'stream-added',
'stream-removed',
'peer-online',
'peer-leave',
'stream-fallback'
]
const webClient = roomStore.rtcClient as AgoraWebClient;
for (let eventName of events) {
webClient.rtc.off(eventName, () => {});
}
console.log("[agora-web] rtmClient logout");
await roomStore.rtmClient.logout();
console.log("[agora-web] remove event listener");
webClient.exit().then(() => {
console.log("[agora-web] do remove event listener");
}).catch(console.warn)
.finally(() => {
this.removeLocalStream();
});
}
async exitAll() { async exitAll() {
console.log('exitAll'); console.log('exitAll');
...@@ -1471,33 +1616,11 @@ export class RoomStore { ...@@ -1471,33 +1616,11 @@ export class RoomStore {
} }
await this.rtmClient.logout(); await this.rtmClient.logout();
const webClient = roomStore.rtcClient as AgoraWebClient; const webClient = roomStore.rtcClient as AgoraWebClient;
const events = [
'onTokenPrivilegeWillExpire',
'onTokenPrivilegeDidExpire',
'error',
'stream-published',
'stream-subscribed',
'stream-added',
'stream-removed',
'peer-online',
'peer-leave',
'stream-fallback'
]
for (let eventName of events) {
webClient.rtc.off(eventName, () => {});
}
console.log("[agora-web] rtmClient logout");
await roomStore.rtmClient.logout();
console.log("[agora-web] remove event listener");
webClient.exit().then(() => {
console.log("[agora-web] do remove event listener");
GlobalStorage.clear('agora_room');
}).catch(console.warn)
.finally(() => {
roomStore.removeLocalStream();
});
// await this.rtcClient.exit();
GlobalStorage.clear('agora_room'); GlobalStorage.clear('agora_room');
await this.removeRtcListener();
// await this.rtcClient.exit();
} catch(err) { } catch(err) {
// debugger // debugger
console.warn('exitAll', err); console.warn('exitAll', err);
......
...@@ -46,7 +46,7 @@ const clientEvents: string[] = [ ...@@ -46,7 +46,7 @@ const clientEvents: string[] = [
export const APP_ID = process.env.REACT_APP_AGORA_APP_ID as string; export const APP_ID = process.env.REACT_APP_AGORA_APP_ID as string;
export const APP_TOKEN = process.env.REACT_APP_AGORA_APP_TOKEN as string; export const APP_TOKEN = process.env.REACT_APP_AGORA_APP_TOKEN as string;
export const ENABLE_LOG = process.env.REACT_APP_AGORA_LOG as string === "true"; export const ENABLE_LOG = process.env.REACT_APP_AGORA_LOG as string === "true";
export const SHARE_ID = 7; export const SHARE_ID = 999;
class AgoraRTCClient { class AgoraRTCClient {
...@@ -60,7 +60,7 @@ class AgoraRTCClient { ...@@ -60,7 +60,7 @@ class AgoraRTCClient {
public _localStream: any = null; public _localStream: any = null;
public _streamEvents: string[]; public _streamEvents: string[];
public _clientEvents: string[]; public _clientEvents: string[];
// public _enableBeauty = false;
constructor () { constructor () {
this.streamID = null; this.streamID = null;
this._streamEvents = []; this._streamEvents = [];
...@@ -101,6 +101,7 @@ class AgoraRTCClient { ...@@ -101,6 +101,7 @@ class AgoraRTCClient {
for (let evtName of clientEvents) { for (let evtName of clientEvents) {
this._clientEvents.push(evtName); this._clientEvents.push(evtName);
this._client.on(evtName, (args: any) => { this._client.on(evtName, (args: any) => {
// console.log("[agora-web] " +evtName+ ": ", args);
if (evtName === "peer-leave") { if (evtName === "peer-leave") {
console.log("[agora-web] peer-leave: ", args); console.log("[agora-web] peer-leave: ", args);
} }
...@@ -175,6 +176,12 @@ class AgoraRTCClient { ...@@ -175,6 +176,12 @@ class AgoraRTCClient {
if (!this._published || !this._localStream) { if (!this._published || !this._localStream) {
return resolve(); return resolve();
} }
if (this._localStream ) {
if (this._localStream._enableBeauty) {
this._localStream.setBeautyEffectOptions(false);
this._localStream._enableBeauty = false
}
}
this._client.unpublish(this._localStream, (err: any) => { this._client.unpublish(this._localStream, (err: any) => {
reject(err); reject(err);
}) })
...@@ -199,9 +206,22 @@ class AgoraRTCClient { ...@@ -199,9 +206,22 @@ class AgoraRTCClient {
createLocalStream(data: AgoraStreamSpec): Promise<any> { createLocalStream(data: AgoraStreamSpec): Promise<any> {
this._localStream = AgoraRTC.createStream({...data, mirror: false}); this._localStream = AgoraRTC.createStream({...data, mirror: false});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this._localStream.init(() => { this._localStream.init(async () => {
this.streamID = data.streamID; this.streamID = data.streamID;
this.subscribeLocalStreamEvents(); this.subscribeLocalStreamEvents();
// debugger
if (roomStore.state.me.role == 'teacher') {
const result = await this._localStream.setBeautyEffectOptions(true, {
lighteningContrastLevel: 2,
lighteningLevel: 0.9,
rednessLevel: 0.2,
smoothnessLevel: 0.9
})
this._localStream._enableBeauty = true;
// this._enableBeauty = true;
console.log('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%', result)
}
if (data.audioOutput && data.audioOutput.deviceId) { if (data.audioOutput && data.audioOutput.deviceId) {
this.setAudioOutput(data.audioOutput.deviceId).then(() => { this.setAudioOutput(data.audioOutput.deviceId).then(() => {
resolve(); resolve();
...@@ -234,10 +254,13 @@ class AgoraRTCClient { ...@@ -234,10 +254,13 @@ class AgoraRTCClient {
}) })
} }
async leave () { console.log(9999, 'rtc leave'); async leave () { console.log(9999, 'rtc leave', roomStore.state.me);
if (this._client) { if (this._client) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this._client.leave(resolve, reject); this._client.leave(() => {
resolve()
}, reject);
}) })
} }
} }
...@@ -337,6 +360,7 @@ export default class AgoraWebClient { ...@@ -337,6 +360,7 @@ export default class AgoraWebClient {
await this.rtc.createClient(APP_ID, true); await this.rtc.createClient(APP_ID, true);
await this.rtc.join(this.localUid, channel, token); await this.rtc.join(this.localUid, channel, token);
dual && await this.rtc.enableDualStream(); dual && await this.rtc.enableDualStream();
// console.log('555555555555555555555')
// 改成手动发布 // 改成手动发布
// roomStore.setRTCJoined(true); // roomStore.setRTCJoined(true);
this.joined = true; this.joined = true;
......
...@@ -19,7 +19,9 @@ export enum RoomMessage { ...@@ -19,7 +19,9 @@ export enum RoomMessage {
muteChat = 109, muteChat = 109,
unmuteChat = 110, unmuteChat = 110,
muteBoard = 200, muteBoard = 200,
unmuteBoard = 201 unmuteBoard = 201,
stageUp = 301,
stageDown = 302,
} }
export interface MessageBody { export interface MessageBody {
......
...@@ -25,4 +25,4 @@ export class CustomStorage { ...@@ -25,4 +25,4 @@ export class CustomStorage {
} }
const GlobalStorage = new CustomStorage(); const GlobalStorage = new CustomStorage();
export default GlobalStorage; export default GlobalStorage;
\ No newline at end of file
...@@ -165,6 +165,12 @@ export function resolveMessage(peerId: string, { cmd, text }: { cmd: number, tex ...@@ -165,6 +165,12 @@ export function resolveMessage(peerId: string, { cmd, text }: { cmd: number, tex
case RoomMessage.unmuteVideo: case RoomMessage.unmuteVideo:
type = 'unmute video' type = 'unmute video'
break; break;
case RoomMessage.stageDown:
type = 'stage down'
break;
case RoomMessage.stageUp:
type = 'stage up'
break;
default: default:
return console.warn(`[RoomMessage] unknown type, from peerId: ${peerId}`); return console.warn(`[RoomMessage] unknown type, from peerId: ${peerId}`);
} }
...@@ -334,6 +340,7 @@ export function resolveChannelAttrs(json: object) { ...@@ -334,6 +340,7 @@ export function resolveChannelAttrs(json: object) {
shared_uid: student.shared_uid, shared_uid: student.shared_uid,
whiteboard_uid: student.whiteboard_uid, whiteboard_uid: student.whiteboard_uid,
grant_board: +student.grant_board, grant_board: +student.grant_board,
last_stage_ts: +student.last_stage_ts
}); });
} }
// console.log('resolveChannelAttrs 3', accounts); // console.log('resolveChannelAttrs 3', accounts);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment