Commit 6236ec53 authored by liujiaxin's avatar liujiaxin

台上位置固定6人。下台为空闲状态

parent da303d2d
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
"electron:copy:web": "cross-env REACT_APP_RUNTIME_PLATFORM=electron react-app-rewired build", "electron:copy:web": "cross-env REACT_APP_RUNTIME_PLATFORM=electron react-app-rewired build",
"electron:copy:electron": "cpx ./app/**/*.js ./build", "electron:copy:electron": "cpx ./app/**/*.js ./build",
"electron:build": "npm run electron:copy:web && npm run electron:copy:electron", "electron:build": "npm run electron:copy:web && npm run electron:copy:electron",
"dev": "cross-env REACT_APP_RUNTIME_PLATFORM=web react-app-rewired start", "dev": "cross-env REACT_APP_RUNTIME_PLATFORM=web react-app-rewired start ",
"build": "cross-env REACT_APP_RUNTIME_PLATFORM=web react-app-rewired build", "build": "cross-env REACT_APP_RUNTIME_PLATFORM=web react-app-rewired build",
"test": "react-app-rewired test --env=jsdom", "test": "react-app-rewired test --env=jsdom",
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "source-map-explorer 'build/static/js/*.js'",
......
...@@ -36,8 +36,8 @@ const useStyles = makeStyles({ ...@@ -36,8 +36,8 @@ const useStyles = makeStyles({
} }
}); });
const CourseWareFrame: React.FC = () => { const CourseWareFrame: React.FC = () => {
const classes = useStyles(); const classes = useStyles();
// `${ENDPOINT}/airclass_ol?token=${this.userToken}`;
const src = endPoint.getCourseUrl(); const src = endPoint.getCourseUrl();
return ( return (
<div className={classes.style1}> <div className={classes.style1}>
......
...@@ -83,8 +83,14 @@ const DialogContainer = () => { ...@@ -83,8 +83,14 @@ const DialogContainer = () => {
const onConfirm = (type: string) => { const onConfirm = (type: string) => {
if (type === 'exitRoom') { if (type === 'exitRoom') {
globalStore.removeDialog(); // debugger
history.push('/'); globalStore.showLoading();
roomStore.exitAll().then(() => {
globalStore.stopLoading();
globalStore.removeDialog();
history.push('/');
})
} }
else if (type === 'apply') { else if (type === 'apply') {
Promise.all([ Promise.all([
...@@ -103,16 +109,16 @@ const DialogContainer = () => { ...@@ -103,16 +109,16 @@ const DialogContainer = () => {
} }
return ( return (
visible ? visible ?
<RoomDialog <RoomDialog
type={dialog.type} type={dialog.type}
desc={dialog.message} desc={dialog.message}
onClose={onClose} onClose={onClose}
onConfirm={onConfirm} onConfirm={onConfirm}
/> : /> :
null null
) )
} }
export default React.memo(DialogContainer); export default React.memo(DialogContainer);
\ No newline at end of file
...@@ -231,7 +231,8 @@ const MemberHolder: React.FC<MemberHolderProps> = ({ ...@@ -231,7 +231,8 @@ const MemberHolder: React.FC<MemberHolderProps> = ({
{account ? {account ?
<div className="user-profile"> <div className="user-profile">
<span className="account">{account}</span> <span className="account">{account}</span>
{me.uid === id || me.role === 'teacher' ? {/*me.uid === id ||*/}
{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}
......
...@@ -13,7 +13,9 @@ import { isElectron, platform } from '../utils/platform'; ...@@ -13,7 +13,9 @@ import { isElectron, platform } from '../utils/platform';
import { useRoomState } from '../containers/root-container'; import { useRoomState } from '../containers/root-container';
import { roomStore } from '../stores/room'; import { roomStore } from '../stores/room';
import { globalStore } from '../stores/global'; import { globalStore } from '../stores/global';
import VoiceOverOffIcon from '@material-ui/icons/VoiceOverOff';
import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver';
import RoomToolsBar from "./room-tools-bar";
interface NavProps { interface NavProps {
delay: string delay: string
network: string network: string
...@@ -41,7 +43,7 @@ export function Nav ({ ...@@ -41,7 +43,7 @@ export function Nav ({
}: NavProps) { }: NavProps) {
const {NavBtn} = usePlatform(); const {NavBtn} = usePlatform();
const handleFinish = (evt: any) => { const handleFinish = (evt: any) => {
onCardConfirm('setting'); onCardConfirm('setting');
} }
...@@ -51,7 +53,7 @@ export function Nav ({ ...@@ -51,7 +53,7 @@ export function Nav ({
<div className={`nav-container ${isElectron ? 'draggable' : ''}`}> <div className={`nav-container ${isElectron ? 'draggable' : ''}`}>
<div className="class-title"> <div className="class-title">
<span className="room-name">{roomName}</span> <span className="room-name">{roomName}</span>
{role === 'teacher' ? {role === 'teacher' ?
<Button className={`nav-button ${classState ? "stop" : "start"}`} name={classState ? "Class end" : "Class start"} onClick={(evt: any) => { <Button className={`nav-button ${classState ? "stop" : "start"}`} name={classState ? "Class end" : "Class start"} onClick={(evt: any) => {
handleClick("classState") handleClick("classState")
}} /> : null} }} /> : null}
...@@ -68,18 +70,21 @@ export function Nav ({ ...@@ -68,18 +70,21 @@ export function Nav ({
<span className="time">{moment.utc(time).format('HH:mm:ss')}</span> <span className="time">{moment.utc(time).format('HH:mm:ss')}</span>
</div> </div>
<span className="menu-split" /> <span className="menu-split" />
<div className={platform === 'web' ? "btn-group" : 'electron-btn-group' }>
{ roomStore.state.me.role == 'teacher' ? <RoomToolsBar onClick={handleClick} /> : null}
{/*<div className={platform === 'web' ? "btn-group" : 'electron-btn-group' }>
{platform === 'web' ? <Icon className="icon-setting" onClick={(evt: any) => { {platform === 'web' ? <Icon className="icon-setting" onClick={(evt: any) => {
handleClick("setting"); handleClick("setting");
}}/> : null} }}/> : null}
<Icon className="icon-exit" onClick={(evt: any) => { <Icon className="icon-exit" onClick={(evt: any) => {
handleClick("exit"); handleClick("exit");
}} /> }} />
</div> </div> */}
<NavBtn /> <NavBtn />
</div> </div>
</div> </div>
{showSetting ? {showSetting ?
<SettingCard className="internal-card" <SettingCard className="internal-card"
handleFinish={handleFinish} /> : null handleFinish={handleFinish} /> : null
} }
...@@ -228,7 +233,7 @@ export default function NavContainer() { ...@@ -228,7 +233,7 @@ export default function NavContainer() {
} }
return ( return (
<Nav <Nav
role={me.role} role={me.role}
roomName={course.roomName} roomName={course.roomName}
classState={Boolean(course.courseState)} classState={Boolean(course.courseState)}
...@@ -241,4 +246,4 @@ export default function NavContainer() { ...@@ -241,4 +246,4 @@ export default function NavContainer() {
handleClick={handleClick} handleClick={handleClick}
/> />
) )
} }
\ No newline at end of file
.room-tool-box{ .room-tool-box{
right: 10px;
width: 46px;
border: 1px solid #dbe2e5;
bottom: 10px;
cursor: pointer;
display: flex; display: flex;
z-index: 9; flex-direction: row;
position: absolute;
//background: #fff;
box-shadow: 0 2px 4px 0 rgba(0,0,0,.1);
box-sizing: border-box;
align-items: center;
line-height: 42;
user-select: none;
border-radius: 6px;
top: 10px;
flex-direction: column;
height: 90px;
padding: 3px;
background-color: rgba(51, 51, 51, 0.4);
.tool-btn{ .tool-btn{
cursor: pointer; cursor: pointer;
display: flex; display: flex;
height: 38px; height: 32px;
width: 38px; width: 32px;
margin: 2px; margin: 4px;
border-radius: 4px; border-radius: 4px;
background: #565656; //background: #565656;
box-sizing: border-box; box-sizing: border-box;
color: #eee; //color: #eee;
svg { svg {
width: 38px; width: 30px;
height: 38px; height: 30px;
display: block; display: block;
} }
} }
.tool-btn:hover{
background-color: rgba(0, 0, 0, 0.07);
border-radius: 4px;
}
} }
...@@ -6,9 +6,38 @@ import { VolumeOff, VolumeUp, ImportExport } from '@material-ui/icons'; ...@@ -6,9 +6,38 @@ import { VolumeOff, VolumeUp, ImportExport } from '@material-ui/icons';
import {roomStore} from "../stores/room"; import {roomStore} from "../stores/room";
import {useRoomState} from "../containers/root-container"; import {useRoomState} from "../containers/root-container";
import './room-tools-bar.scss'; import './room-tools-bar.scss';
import VoiceOverOffIcon from "@material-ui/icons/VoiceOverOff";
import RecordVoiceOverIcon from "@material-ui/icons/RecordVoiceOver";
import Category from "@material-ui/icons/Category";
import ImportContacts from "@material-ui/icons/ImportContacts";
import SettingsApplicationsIcon from '@material-ui/icons/SettingsApplications';
import SettingsPowerIcon from '@material-ui/icons/SettingsPower';
import {TOOL_TYPE} from "../utils/types";
type Props = {
// zoomScale: number
// zoomChange: (scale: number) => void
onClick: (type: string) => void
// onClickBoardLock: () => void
};
export default function RoomToolsBar (props: any) { export default function RoomToolsBar (props: any) {
// export const RoomToolsBar: React.FC<Props> = ({onClick}) => {
const roomState = useRoomState(); const roomState = useRoomState();
const changeTool = async () => {
console.log(roomStore.state.course.currentTool);
if (roomStore.state.course.currentTool == TOOL_TYPE.WHITEBOARD) {
await roomStore.setCurrentTool(TOOL_TYPE.COURSE_WARE)
} else {
await roomStore.setCurrentTool(TOOL_TYPE.WHITEBOARD)
}
}
const {currentTool} = useMemo(() => {
return {
currentTool: roomStore.state.course.currentTool
}
}, [roomState]);
const {muteAudio} = useMemo(() => { const {muteAudio} = useMemo(() => {
return { return {
muteAudio: roomState.course.muteAudio muteAudio: roomState.course.muteAudio
...@@ -25,11 +54,27 @@ export default function RoomToolsBar (props: any) { ...@@ -25,11 +54,27 @@ export default function RoomToolsBar (props: any) {
const randomStageUp = async () => { const randomStageUp = async () => {
await roomStore.randomStageUp() await roomStore.randomStageUp()
} }
return ( return (
<> <>
<div className={'room-tool-box'}> <div className={'room-tool-box'}>
{muteAudio ? <div className="tool-btn" onClick={audioOn}><VolumeUp></VolumeUp></div> : <div onClick={audioOff} className="tool-btn"><VolumeOff></VolumeOff></div> }
<div className="tool-btn"><ImportExport onClick={randomStageUp}></ImportExport></div>
{muteAudio
? <div className="tool-btn" onClick={audioOn}><RecordVoiceOverIcon/></div>
: <div onClick={audioOff} className="tool-btn"><VoiceOverOffIcon/></div>
}
{currentTool == TOOL_TYPE.WHITEBOARD
? <div className="tool-btn" onClick={changeTool}><ImportContacts/></div>
: <div onClick={changeTool} className="tool-btn"><Category/></div>
}
<div className="tool-btn" onClick={(evt: any) => {
props.onClick("setting");
}}><SettingsApplicationsIcon/></div>
<div className="tool-btn" onClick={(evt: any) => {
props.onClick("exit");
}}><SettingsPowerIcon/></div>
{/*<div className="tool-btn"><ImportExport onClick={randomStageUp}></ImportExport></div>*/}
</div> </div>
</> </>
) )
......
...@@ -18,7 +18,30 @@ ...@@ -18,7 +18,30 @@
z-index: 9; z-index: 9;
} }
} }
.menu-close {
cursor: pointer;
width: 18px;
top: 15px;
right: 15px;
position: absolute;
background-size: 18px;
height: 18px;
border-radius: 8px;
&:hover {
background: rgba(0, 0, 0, 0.07);
}
&::after {
border-radius: 8px;
display: block;
width: 18px;
height: 18px;
content: ' ';
background-position: center;
background-repeat: no-repeat;
background-size: 18px;
background-image: url('../assets/icon-resource-menu-close.png');
}
}
.video-profile { .video-profile {
z-index: 3; z-index: 3;
width: 100%; width: 100%;
......
...@@ -4,6 +4,21 @@ import './video-player.scss'; ...@@ -4,6 +4,21 @@ import './video-player.scss';
import { AgoraElectronStream, StreamType, nativeRTCClient as nativeClient } from '../utils/agora-electron-client'; import { AgoraElectronStream, StreamType, nativeRTCClient as nativeClient } from '../utils/agora-electron-client';
import { useRoomState } from '../containers/root-container'; import { useRoomState } from '../containers/root-container';
import { platform } from '../utils/platform'; import { platform } from '../utils/platform';
import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward';
import {makeStyles} from '@material-ui/core/styles';
const useStyles = makeStyles({
stageDown : {
display: 'flex',
width: 18,
height: 24,
color: '#fff',
lineHeight: 24,
opacity: .8,
cursor:'pointer'
}
});
const contentMode = 0; const contentMode = 0;
...@@ -41,6 +56,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ...@@ -41,6 +56,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
handleClose, handleClose,
close close
}) => { }) => {
const style = useStyles();
const loadVideo = useRef<boolean>(false); const loadVideo = useRef<boolean>(false);
const loadAudio = useRef<boolean>(false); const loadAudio = useRef<boolean>(false);
...@@ -219,6 +235,12 @@ const onAudioClick = (evt: any) => { ...@@ -219,6 +235,12 @@ const onAudioClick = (evt: any) => {
handleClick('audio', streamID, id); handleClick('audio', streamID, id);
} }
} }
const onStageDownClick = (evt: any) => {
if (handleClick && id) {
handleClick('stageDown', streamID, id);
}
}
const onVideoClick = (evt: any) => { const onVideoClick = (evt: any) => {
if (handleClick && id) { if (handleClick && id) {
...@@ -242,17 +264,30 @@ return ( ...@@ -242,17 +264,30 @@ return (
account ? account ?
<div className="video-profile"> <div className="video-profile">
<span className="account">{account}</span> <span className="account">{account}</span>
{me.uid === id || me.role === 'teacher' ? {streamID <= 0 ? null :
<span className="media-btn"> <div>
<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"} /> me.uid === id || me.role === 'teacher'
</span> : ? <span className="media-btn">
<span className="media-btn"> {me.role === 'teacher' && me.uid !== id ?
<Icon className={audio ? "icon-speaker-on disabled" : "icon-speaker-off disabled"} data={"audio"} /> <ArrowDownwardIcon onClick={onStageDownClick} className={style.stageDown}></ArrowDownwardIcon>
<Icon className={video ? "icons-camera-unmute-s disabled" : "icons-camera-mute-s disabled"} data={"video"} /> : null}
</span> <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"}/>
</span>
: <span className="media-btn">
<Icon className={audio ? "icon-speaker-on disabled" : "icon-speaker-off disabled"} data={"audio"}/>
<Icon className={video ? "icons-camera-unmute-s disabled" : "icons-camera-mute-s disabled"}
data={"video"}/>
</span>
}</div>
} }
</div> </div>
: null : null
} }
......
...@@ -5,6 +5,7 @@ import { WhiteboardState, whiteboard } from '../stores/whiteboard'; ...@@ -5,6 +5,7 @@ import { WhiteboardState, whiteboard } from '../stores/whiteboard';
import { useHistory, useLocation } from 'react-router-dom'; import { useHistory, useLocation } from 'react-router-dom';
import { resolveMessage, resolvePeerMessage, resolveChannelAttrs, jsonParse } from '../utils/helper'; import { resolveMessage, resolvePeerMessage, resolveChannelAttrs, jsonParse } from '../utils/helper';
import GlobalStorage from '../utils/custom-storage'; import GlobalStorage from '../utils/custom-storage';
import {endPoint} from "../services/agora-end-points";
export type IRootProvider = { export type IRootProvider = {
globalState: GlobalState globalState: GlobalState
...@@ -105,9 +106,17 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -105,9 +106,17 @@ export const RootProvider: React.FC<any> = ({children}) => {
}).catch(console.warn); }).catch(console.warn);
}); });
rtmClient.on("AttributesUpdated", (attributes: object) => { rtmClient.on("AttributesUpdated", (attributes: object) => {
if (Object.keys(attributes).length == 0) {
globalStore.showToast({
type: 'rtmClient',
message: 'teacher close classroom'
});
history.push('/');
return;
}
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); console.log('[rtm-client] updated origin attributes', attributes);
roomStore.updateRoomAttrs(channelAttrs) roomStore.updateRoomAttrs(channelAttrs)
}); });
rtmClient.on("MemberJoined", (memberId: string) => { rtmClient.on("MemberJoined", (memberId: string) => {
...@@ -152,6 +161,8 @@ export const RootProvider: React.FC<any> = ({children}) => { ...@@ -152,6 +161,8 @@ export const RootProvider: React.FC<any> = ({children}) => {
me: room.me, me: room.me,
course: room.course, course: room.course,
mediaDevice: room.mediaDevice, mediaDevice: room.mediaDevice,
cwLink: endPoint.userToken,
studentsOrder: room.studentsOrder
}); });
// WARN: DEBUG ONLY MUST REMOVED IN PRODUCTION // WARN: DEBUG ONLY MUST REMOVED IN PRODUCTION
//@ts-ignore //@ts-ignore
......
...@@ -143,17 +143,67 @@ export default function useStream () { ...@@ -143,17 +143,67 @@ export default function useStream () {
console.log(100000, studentsOrder); console.log(100000, studentsOrder);
console.log(11111111, studentIds.toArray()); console.log(11111111, studentIds.toArray());
studentIds = studentIds.sort(function(a, b){ // studentIds = studentIds.sort(function(a, b){
if (studentsOrder.indexOf(+a) < 0) {return 0} // if (studentsOrder.indexOf(+a) < 0) {return 0}
if (studentsOrder.indexOf(+b) < 0) {return 0} // if (studentsOrder.indexOf(+b) < 0) {return 0}
return studentsOrder.indexOf(+a) - studentsOrder.indexOf(+b); // return studentsOrder.indexOf(+a) - studentsOrder.indexOf(+b);
}); // });
console.log(222222, studentIds.toArray()); const sids: any = [];
for (const ouid of studentsOrder) {
if (ouid == 0) {
sids.push(0)
} else {
// find in peer
const uid = studentIds.find((it: number) => `${ouid}` == `${it}`);
if (uid) {
sids.push(uid);
} else {
// find in channel
const usr = userAttrs.get(`${uid}`)
if (usr) {
sids.push(+usr.uid);
} else {
sids.push(-1);
}
}
}
}
console.log(222222, userAttrs.toArray());
console.log(333333, sids);
// capture all remote streams // capture all remote streams
for (let studentId of studentIds) { for (let studentId of /*studentIds*/sids) {
console.log('*****************', studentId)
if (studentId == undefined) {
continue
}
if (studentId == 0) {
const _tmpStream = {
account: '空闲',
streamID: studentId,
video: 0,
audio: 0,
local: false,
}
studentStreams.push(_tmpStream);
continue
}
if (studentId == -1) {
const _tmpStream = {
account: '等待中...',
streamID: studentId,
video: 0,
audio: 0,
local: false,
}
studentStreams.push(_tmpStream);
continue
}
if (me.role === 'student' && +me.uid == studentId) { if (me.role === 'student' && +me.uid == studentId) {
if (myAttr && roomState.rtc.localStream) { if (myAttr && roomState.rtc.localStream) {
console.log('use local stream') console.log('use local stream')
...@@ -168,6 +218,7 @@ export default function useStream () { ...@@ -168,6 +218,7 @@ export default function useStream () {
continue continue
} }
} }
const studentAttr = userAttrs.get(''+studentId); const studentAttr = userAttrs.get(''+studentId);
const stream = roomState.rtc.remoteStreams.get(+studentId); const stream = roomState.rtc.remoteStreams.get(+studentId);
if (studentAttr) { if (studentAttr) {
...@@ -190,7 +241,7 @@ export default function useStream () { ...@@ -190,7 +241,7 @@ export default function useStream () {
} }
} }
userAttrs.forEach((v, k) => { userAttrs.forEach((v, k) => {
if (!studentIds.includes(+v.uid)) { if (!sids.includes(+v.uid)) {
if (`${v.uid}` != `${teacherUid}`) { if (`${v.uid}` != `${teacherUid}`) {
const s: any = { const s: any = {
streamID: +v.uid, streamID: +v.uid,
...@@ -210,7 +261,7 @@ export default function useStream () { ...@@ -210,7 +261,7 @@ export default function useStream () {
}, [ }, [
course, course,
me.uid, me.uid,
roomStore.state.studentsOrder, // roomStore.state.studentsOrder,
roomState.rtc.users, roomState.rtc.users,
roomState.rtc.remoteStreams, roomState.rtc.remoteStreams,
roomState.rtc.localStream roomState.rtc.localStream
......
...@@ -95,7 +95,7 @@ body { ...@@ -95,7 +95,7 @@ body {
background: #F5F7F8; background: #F5F7F8;
border-radius:8px 0px 0px 8px; border-radius:8px 0px 0px 8px;
position: relative; position: relative;
.cover-placeholder { .cover-placeholder {
display: block; display: block;
background-repeat: no-repeat; background-repeat: no-repeat;
...@@ -288,13 +288,13 @@ body { ...@@ -288,13 +288,13 @@ body {
@media screen and (max-width: 780px) { @media screen and (max-width: 780px) {
.home-cover-web { .home-cover-web {
min-width: 780px; //min-width: 780px;
} }
} }
@media screen and (max-height: 768px) { @media screen and (max-height: 768px) {
.home-cover-web { .home-cover-web {
min-height: 768px; //min-height: 768px;
} }
} }
...@@ -322,4 +322,4 @@ body { ...@@ -322,4 +322,4 @@ body {
} }
} }
} }
} }
\ No newline at end of file
...@@ -213,7 +213,7 @@ export function RoomPage({ children }: any) { ...@@ -213,7 +213,7 @@ export function RoomPage({ children }: any) {
]); ]);
useEffect(() => { useEffect(() => {
debugger // debugger
if (!roomState.me.uid || !roomState.course.rid) return; if (!roomState.me.uid || !roomState.course.rid) return;
if (classroom) { if (classroom) {
if (platform === 'web') { if (platform === 'web') {
...@@ -301,7 +301,7 @@ export function RoomPage({ children }: any) { ...@@ -301,7 +301,7 @@ export function RoomPage({ children }: any) {
}) })
*/ */
rtc.current = true; rtc.current = true;
debugger // debugger
// WARN: IF YOU ENABLED APP CERTIFICATE, PLEASE SIGN YOUR TOKEN IN YOUR SERVER SIDE AND OBTAIN IT FROM YOUR OWN TRUSTED SERVER API // WARN: IF YOU ENABLED APP CERTIFICATE, PLEASE SIGN YOUR TOKEN IN YOUR SERVER SIDE AND OBTAIN IT FROM YOUR OWN TRUSTED SERVER API
console.info(1.1, 'rtc join') console.info(1.1, 'rtc join')
// if (roomState.me.role == 'teacher') { // if (roomState.me.role == 'teacher') {
...@@ -311,7 +311,7 @@ export function RoomPage({ children }: any) { ...@@ -311,7 +311,7 @@ export function RoomPage({ children }: any) {
uid: +roomState.me.uid, uid: +roomState.me.uid,
channel: roomState.course.rid, channel: roomState.course.rid,
token: '', token: '',
dual: isSmallClass dual: true
}).then(() => { }).then(() => {
console.info(2, 'rtc join ok') console.info(2, 'rtc join ok')
roomStore.publishMeStream(); roomStore.publishMeStream();
...@@ -452,10 +452,11 @@ export function RoomPage({ children }: any) { ...@@ -452,10 +452,11 @@ export function RoomPage({ children }: any) {
console.log(1.15,'stagedown?', 222222 ); console.log(1.15,'stagedown?', 222222 );
roomStore.unPublishMeStream(); roomStore.unPublishMeStream();
} }
console.log(1.2 ,studentsOrder, 'studentsOrder changed publish stream');
} }
console.log(1.2 ,studentsOrder, 'studentsOrder changed publish stream');
}, [studentsOrder]); }, [studentsOrder]);
......
import React, {useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; import React, {useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
// import VideoMarquee from '../../components/video-marquee'; // import VideoMarquee from '../../components/video-marquee';
import MediaBoard from '../../components/mediaboard'; import MediaBoard from '../../components/mediaboard';
import Roomboard from '../../components/roomboard'; // import Roomboard from '../../components/roomboard';
import './small-class.scss'; import './small-class.scss';
import CourseWareFrame from "../../components/courseware-frame"; import CourseWareFrame from "../../components/courseware-frame";
import VideoShowList from "../../components/video-stage-list"; import VideoShowList from "../../components/video-stage-list";
import {ToolsSwitcherController} from "../../components/tools-switcher-controller"; import {ToolsSwitcherController} from "../../components/tools-switcher-controller";
import {roomStore} from "../../stores/room"; import {roomStore} from "../../stores/room";
import {TOOL_TYPE} from "../../utils/types"; import {TOOL_TYPE} from "../../utils/types";
import {platform} from "../../utils/platform"; // import {platform} from "../../utils/platform";
import AgoraWebClient from "../../utils/agora-rtc-client"; // import AgoraWebClient from "../../utils/agora-rtc-client";
import {AgoraElectronClient} from "../../utils/agora-electron-client"; // import {AgoraElectronClient} from "../../utils/agora-electron-client";
import {useRoomState} from "../../containers/root-container"; import {useRoomState} from "../../containers/root-container";
import RoomToolsBar from "../../components/room-tools-bar"; // import RoomToolsBar from "../../components/room-tools-bar";
export default function SmallClass() { export default function SmallClass() {
const [tool, setTool] = useState(true); const [tool, setTool] = useState(true);
...@@ -39,15 +39,15 @@ export default function SmallClass() { ...@@ -39,15 +39,15 @@ export default function SmallClass() {
{/*<VideoMarquee />*/} {/*<VideoMarquee />*/}
<VideoShowList/> <VideoShowList/>
<div className="container"> <div className="container">
{roomStore.state.me.role == 'teacher' ?<ToolsSwitcherController onClick={() => { {/*{roomStore.state.me.role == 'teacher' ?<ToolsSwitcherController onClick={() => {*/}
changeTool(); {/* changeTool();*/}
}}/> : null} {/*}}/> : null}*/}
{currentTool == TOOL_TYPE.WHITEBOARD {currentTool == TOOL_TYPE.WHITEBOARD
? <MediaBoard /> ? <MediaBoard />
: <CourseWareFrame></CourseWareFrame>} : <CourseWareFrame></CourseWareFrame>}
{ roomStore.state.me.role == 'teacher' ? <RoomToolsBar /> : null}
{/*{ roomStore.state.me.role == 'teacher' ? <Roomboard currentActive={'media'} /> : null}*/} {/*{ roomStore.state.me.role == 'teacher' ? <Roomboard currentActive={'media'} /> : null}*/}
</div> </div>
</div> </div>
......
...@@ -79,17 +79,70 @@ function HomePage() { ...@@ -79,17 +79,70 @@ function HomePage() {
setRequired({...required, yourPass: 'missing your password'}); setRequired({...required, yourPass: 'missing your password'});
return; return;
} }
///////////////////////////////
/*
session.roomName = "test_01__123";// userInfo["class_id"];
// session.yourName = userInfo["nick_name"];
session.role = `${session.yourName}`==="1"?"teacher":"student";
session.roomType = 1;
const path = roomTypes[session.roomType].path
const payload = {
uid: `${session.yourName}`, // genUid(),
rid: `${session.roomName}`, // `${session.roomType}${MD5(session.roomName)}`,
role: session.role,
roomName: session.roomName,
roomType: session.roomType,
video: 0,
audio: 0,
chat: 1,
account: session.yourName,
token: '',
boardId: '',
linkId: 0,
sharedId: 0,
lockBoard: 0,
}
ref.current = true;
globalStore.showLoading();
roomStore.loginAndJoin(payload).then(() => {
roomStore.updateSessionInfo(payload);
history.push(`/classroom/${path}`);
}).catch((err: any) => {
if (err.reason) {
globalStore.showToast({
type: 'rtmClient',
message: err.reason
})
} else {
globalStore.showToast({
type: 'rtmClient',
message: 'login failure, please checkout ur network'
})
}
console.warn(err);
})
.finally(() => {
ref.current = false;
globalStore.stopLoading();
})
*/
///////////////////////////////
endPoint.login({username: session.yourName, password: session.yourPass}).then((userInfo: any) => { endPoint.login({username: session.yourName, password: session.yourPass}).then((userInfo: any) => {
console.log(userInfo); console.log(userInfo);
session.roomName = userInfo["class_id"]; session.roomName = userInfo["class_id"];// userInfo["class_id"];
session.yourName = userInfo["nick_name"]; // session.yourName = userInfo["nick_name"];
session.role = userInfo["classRole"]==="tea"?"teacher":"student"; session.role = userInfo["classRole"]==="tea"?"teacher":"student";
session.roomType = 1; session.roomType = 1;
if (!roomTypes[session.roomType]) return; if (!roomTypes[session.roomType]) return;
const path = roomTypes[session.roomType].path const path = roomTypes[session.roomType].path
const payload = { const payload = {
uid: `${userInfo["id"]}`, // genUid(), uid: userInfo["classRole"]==="tea"?"100":`${session.yourName.substr(-2)}`, // `${userInfo["id"]}`, // genUid(),
rid: `${session.roomName}`, // `${session.roomType}${MD5(session.roomName)}`, rid: `${session.roomName}`, // `${session.roomType}${MD5(session.roomName)}`,
role: session.role, role: session.role,
roomName: session.roomName, roomName: session.roomName,
...@@ -133,6 +186,8 @@ function HomePage() { ...@@ -133,6 +186,8 @@ function HomePage() {
message: err message: err
}) })
}); });
} }
return ( return (
......
import { AgoraFetch } from "../utils/fetch"; import { AgoraFetch } from "../utils/fetch";
import jwt_decode from 'jwt-decode'; import jwt_decode from 'jwt-decode';
import {roomStore} from "../stores/room";
const ENDPOINT: string = process.env.REACT_APP_AGORA_ENDPOINT as string; const ENDPOINT: string = process.env.REACT_APP_AGORA_ENDPOINT as string;
...@@ -78,6 +79,10 @@ export class EndPoint { ...@@ -78,6 +79,10 @@ export class EndPoint {
} }
getCourseUrl(){ getCourseUrl(){
if (!this.userToken) {
this.userToken = roomStore.state.cwLink;
}
console.log('getCourseUrl', this.userToken)
return `${ENDPOINT}/airclass_ol?token=${this.userToken}`; return `${ENDPOINT}/airclass_ol?token=${this.userToken}`;
} }
......
...@@ -17,14 +17,14 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any, ...@@ -17,14 +17,14 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any,
permitted: true, permitted: true,
reason: '' reason: ''
} }
const channelCountLimit = [2, 17, 17]; // const channelCountLimit = [2, 17, 17];
//
let maximum = channelCountLimit[roomType]; // let maximum = channelCountLimit[roomType];
if (channelCount >= maximum) { // if (channelCount >= maximum) {
result.permitted = false; // result.permitted = false;
result.reason = 'The number of students and teacher have reached upper limit'; // result.reason = 'The number of students and teacher have reached upper limit';
return result; // return result;
} // }
const teacher = get(onlineStatus, 'teacher', false); const teacher = get(onlineStatus, 'teacher', false);
const totalCount: number = get(onlineStatus, 'totalCount', 0); const totalCount: number = get(onlineStatus, 'totalCount', 0);
...@@ -38,13 +38,13 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any, ...@@ -38,13 +38,13 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any,
} }
} }
if (role === 'student') { // if (role === 'student') {
if (totalCount+1 > maximum) { // if (totalCount+1 > maximum) {
result.permitted = false; // result.permitted = false;
result.reason = 'Student have reached upper limit'; // result.reason = 'Student have reached upper limit';
return result; // return result;
} // }
} // }
return result; return result;
} }
...@@ -124,6 +124,7 @@ export type RoomState = { ...@@ -124,6 +124,7 @@ export type RoomState = {
mediaDevice: MediaDeviceState mediaDevice: MediaDeviceState
messages: List<ChatMessage> messages: List<ChatMessage>
studentsOrder: number[] studentsOrder: number[]
cwLink: string,
} }
export type AgoraMediaStream = { export type AgoraMediaStream = {
...@@ -289,212 +290,92 @@ export class RoomStore { ...@@ -289,212 +290,92 @@ export class RoomStore {
// @ts-ignore // @ts-ignore
window['roomStore'] = this; window['roomStore'] = this;
} }
/*
async stageUpRtc(uid: string) { async stageDown(uid: string) {
let store = this._state.rtc.users; // debugger
let itemsList = store.toList(); //把台上位置变零
const idx = itemsList.indexOf(+uid); let studentsOrder: any[] = [...this.state.studentsOrder];
if (idx < STAGE_NUM) { const uidIndex = studentsOrder.indexOf(+uid)
return; if (uidIndex > -1) {
} studentsOrder.splice(uidIndex, 1, 0);
itemsList = itemsList.insert(0, +uid); const data: any = {};
const downVal = itemsList.get(STAGE_NUM); data['studentsOrder'] = studentsOrder;
if (downVal != undefined) { const downUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
itemsList = itemsList.splice(STAGE_NUM, 1).splice(idx, 1, downVal); if (downUser) {
store = OrderedSet(itemsList) downUser.audio = 0;
} else { downUser.video = 0;
return ; data[uid] = downUser
}
this.state = {
...this.state,
rtc: {
...this.state.rtc,
users: store,
} }
await this.rtmClient.addOrUpdateChannelAttributes(data);
} }
this.commit(this.state);
} }
async stageDownRtc(uid: string) { async stageUp(uid: string) {
let store = this._state.rtc.users; let studentsOrder: any[] = [...this.state.studentsOrder];
console.log(store); const data: any = {};
// let uid = 2; const emptyIndex = studentsOrder.indexOf(0);
// stageNum = 3; if (emptyIndex > -1) {
let itemsList = store.toList(); studentsOrder[emptyIndex] = +uid;
let idx = itemsList.indexOf(+uid); const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
if (idx >= STAGE_NUM) { if (upUser) {
return; upUser.video = 1;
} upUser.audio = 1;
let upVal = itemsList.get(STAGE_NUM); data[upUser.uid] = upUser;
let downVal = itemsList.get(idx); }
if (upVal != undefined && downVal != undefined) {
itemsList = itemsList.splice(STAGE_NUM, 1).splice(idx, 1, upVal).push(downVal)
store = OrderedSet(itemsList)
} else { } else {
return return
} }
this.state = { data['studentsOrder'] = studentsOrder;
...this.state, await this.rtmClient.addOrUpdateChannelAttributes(data);
rtc: {
...this.state.rtc,
users: store,
}
}
this.commit(this.state);
} }
async stageUp1(uid: string) { async stageUp3333(uid: string) {
const attrs = await this.rtmClient.getChannelAttrs(); debugger
let studentsOrder: any[] = []; let studentsOrder: any[] = [...this.state.studentsOrder];
// if ((attrs as any).studentsOrder && (attrs as any).studentsOrder.value) {
// studentsOrder = JSON.parse((attrs as any).studentsOrder.value) || [];
// }
const data: any = {};
const onlineStatus = JSON.parse(attrs)
const target = jsonParse(get(onlineStatus[uid], 'value'));
// 上台需要开启视频
// if (!+target.video) {
target.video = 1;
data[uid] = JSON.stringify(target)
// }
const tmp: [string, AgoraUser][] =[]; const data: any = {};
this._state.users.forEach((v, k)=>{ const emptyIndex = studentsOrder.indexOf(0);
if (/^[0-9]+$/.test(k)) { if (emptyIndex > -1) {
tmp.push([k, v]) studentsOrder[emptyIndex] = +uid;
const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
if (upUser) {
upUser.video = 1;
upUser.audio = 1;
data[upUser.uid] = upUser;
}
} else {
studentsOrder.unshift(+uid);
let lastUid = studentsOrder[studentsOrder.length - 1];
const lastUser: AgoraUser | undefined = this.state.users.get(`${lastUid}`);
if (lastUser) {
lastUser.video = 0;
lastUser.audio = 0;
data[lastUser.uid] = lastUser;
} }
})
const uidIdx = tmp.findIndex((o) => {
return `${o[0]}` == `${uid}`
})
const uidObj = tmp[uidIdx]
tmp.splice(uidIdx, 1)
const stageEndObj = tmp[STAGE_NUM - 1]
tmp.splice(STAGE_NUM - 1, 1)
tmp.unshift(uidObj);
tmp.push(stageEndObj)
// 下台关闭音视频
// stageEndObj[1].audio = 0;
// stageEndObj[1].video = 0;
data[stageEndObj[1].uid] = JSON.stringify(stageEndObj[1])
tmp.forEach(o => {
studentsOrder.push(+o[0]);
});
// const queue = {'studentsOrder': JSON.stringify(studentsOrder)};
data['studentsOrder'] = JSON.stringify(studentsOrder)
const aa = await this.rtmClient.addOrUpdateChannelAttributes(data);
console.log(aa)
const users = OrderedMap(tmp);
// this.state = { const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
// ...this.state, if (upUser) {
// users upUser.video = 1;
// } upUser.audio = 1;
data[upUser.uid] = upUser;
}
// this.commit(this.state); if (!lastUser || !upUser) {
} return
async stageDown2(uid: string) { }
const tmp: [string, AgoraUser][] =[];
this._state.users.forEach((v, k)=>{
tmp.push([k, v])
})
console.log(tmp);
const uidIdx = tmp.findIndex((o) => {
return `${o[0]}` == `${uid}`
})
const uidObj = tmp[uidIdx]
const waitUpObj = tmp[STAGE_NUM]
tmp.splice(uidIdx, 1, waitUpObj)
tmp.splice(STAGE_NUM , 1)
tmp.push(uidObj)
const users = OrderedMap(tmp);
this.state = {
...this.state,
users
}
this.commit(this.state);
}
async stageUp2(uid: string) {
const attrs = await this.rtmClient.getChannelAttrs();
let studentsOrder: any[] = [];
// if ((attrs as any).studentsOrder && (attrs as any).studentsOrder.value) {
// studentsOrder = JSON.parse((attrs as any).studentsOrder.value) || [];
// }
const data: any = {};
const onlineStatus = jsonParse(attrs)
let target = jsonParse(get(onlineStatus[uid], 'value'));
if (typeof(target) == 'string') {
target = jsonParse(target)
}
// 上台需要开启视频
// if (!+target.video) {
target.video = 1;
data[uid] = target
// }
const tmp: [string, AgoraUser][] =[]; if (studentsOrder.length > STAGE_NUM) {
this._state.users.forEach((v, k)=>{ studentsOrder.length = STAGE_NUM
if (/^[0-9]+$/.test(k)) {
tmp.push([k, v])
} }
}) }
const uidIdx = tmp.findIndex((o) => {
return `${o[0]}` == `${uid}`
})
const uidObj = tmp[uidIdx]
tmp.splice(uidIdx, 1)
const stageEndObj = tmp[STAGE_NUM - 1]
tmp.splice(STAGE_NUM - 1, 1)
tmp.unshift(uidObj);
tmp.push(stageEndObj)
// 下台关闭音视频
// stageEndObj[1].audio = 0;
// stageEndObj[1].video = 0;
data[stageEndObj[1].uid] = stageEndObj[1]
tmp.forEach(o => {
studentsOrder.push(+o[0]);
});
// const queue = {'studentsOrder': JSON.stringify(studentsOrder)};
data['studentsOrder'] = studentsOrder
const aa = await this.rtmClient.addOrUpdateChannelAttributes(data);
console.log(aa)
const users = OrderedMap(tmp);
// this.state = {
// ...this.state,
// users
// }
// this.commit(this.state);
}
*/
async stageDown(uid: string) {}
async stageUp(uid: string) {
debugger
let studentsOrder: any[] = [...this.state.studentsOrder];
studentsOrder.unshift(+uid);
const lastUid = studentsOrder[studentsOrder.length - 1];
const lastUser: AgoraUser | undefined = this.state.users.get(`${lastUid}`);
const upUser: AgoraUser | undefined = this.state.users.get(`${uid}`);
if (!lastUser || !upUser) {
return
}
lastUser.video = 0;
lastUser.audio = 0;
upUser.video = 1;
upUser.audio = 1;
studentsOrder.length = STAGE_NUM;
const data: any = {};
data[upUser.uid] = upUser;
data[lastUser.uid] = lastUser;
data['studentsOrder'] = studentsOrder; data['studentsOrder'] = studentsOrder;
await this.rtmClient.addOrUpdateChannelAttributes(data); await this.rtmClient.addOrUpdateChannelAttributes(data);
...@@ -511,6 +392,7 @@ export class RoomStore { ...@@ -511,6 +392,7 @@ export class RoomStore {
// this.commit(this.state); // this.commit(this.state);
} }
async randomStageUp() { async randomStageUp() {
const users = roomStore.state.users; const users = roomStore.state.users;
const studentsOrder = roomStore.state.studentsOrder || []; const studentsOrder = roomStore.state.studentsOrder || [];
...@@ -925,8 +807,16 @@ export class RoomStore { ...@@ -925,8 +807,16 @@ 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);
if (payload.role == 'student' && !channelMeta.teacher) {
throw {
type: 'not_permitted',
reason: 'classroom not start yet'
}
return
}
let accounts = channelMeta.accounts; let accounts = channelMeta.accounts;
const onlineStatus = await this.rtmClient.queryOnlineStatusBy(accounts); const onlineStatus = await this.rtmClient.queryOnlineStatusBy(accounts);
/* /*
const me = accounts.find((o)=>{return `${payload.uid}` === `${o.uid}`}); const me = accounts.find((o)=>{return `${payload.uid}` === `${o.uid}`});
if (onlineStatus[payload.uid]) { if (onlineStatus[payload.uid]) {
...@@ -962,10 +852,24 @@ export class RoomStore { ...@@ -962,10 +852,24 @@ export class RoomStore {
}; };
let result = pass === false ? canJoin(argsJoin) : {permitted: true, reason: ''}; let result = pass === false ? canJoin(argsJoin) : {permitted: true, reason: ''};
let studentsOrder: number[] = []; let studentsOrder: number[] = channelMeta.studentsOrder || [];
if (result.permitted) { if (result.permitted) {
// studentsOrder = this.state.studentsOrder; if (payload.role == 'teacher') {
/* // studentsOrder = channelMeta.studentsOrder || [];//this.state.studentsOrder;
if (studentsOrder.length < STAGE_NUM) {
Array(STAGE_NUM - studentsOrder.length).fill(0).forEach((zero) => {
studentsOrder.push(zero)
})
}
} else {
//查看有无空闲
if (!studentsOrder.includes(+payload.uid)) {
const emptyIndex = studentsOrder.indexOf(0);
if (emptyIndex > -1) {
studentsOrder.splice(emptyIndex, 1, +payload.uid);
}
}
/*
if (!studentsOrder.length) { if (!studentsOrder.length) {
accounts.forEach(a => { accounts.forEach(a => {
if (!studentsOrder.includes(+a.uid)) { if (!studentsOrder.includes(+a.uid)) {
...@@ -998,17 +902,21 @@ export class RoomStore { ...@@ -998,17 +902,21 @@ export class RoomStore {
} }
} }
*/ */
let onlineMe = accounts.find((it: any) => `${it.uid}` == `${payload.uid}`);
if (onlineMe) { console.log('login accounts', accounts)
payload.video = onlineMe.video; let onlineMe = accounts.find((it: any) => `${it.uid}` == `${payload.uid}`);
payload.audio = onlineMe.audio; if (onlineMe) {
} else { payload.video = onlineMe.video;
if (channelMeta.studentsOrder && channelMeta.studentsOrder.includes(+payload.uid)) { payload.audio = onlineMe.audio;
payload.video = 1; } else {
payload.audio = 1; if (studentsOrder && studentsOrder.includes(+payload.uid)) {
payload.video = 1;
payload.audio = 1;
}
} }
} }
console.log('login payload', payload)
console.log('login studentsOrder', studentsOrder)
let res = await this.rtmClient.join(rid).then(async r => { let res = await this.rtmClient.join(rid).then(async r => {
this.state = { this.state = {
...@@ -1020,7 +928,7 @@ export class RoomStore { ...@@ -1020,7 +928,7 @@ export class RoomStore {
} }
const grantBoard = role === 'teacher' ? 1 : 0; const grantBoard = role === 'teacher' ? 1 : 0;
console.log(">>>>>>>>>>#room: ", grantBoard); console.log(">>>>>>>>>>#room: ", grantBoard);
await this.updateMe({...payload, grantBoard}); await this.updateMe({...payload, grantBoard}, studentsOrder);
this.commit(this.state); this.commit(this.state);
}); });
// this.state = { // this.state = {
...@@ -1163,7 +1071,7 @@ export class RoomStore { ...@@ -1163,7 +1071,7 @@ export class RoomStore {
await this.rtmClient.addOrUpdateChannelAttributes(data); await this.rtmClient.addOrUpdateChannelAttributes(data);
} }
async updateMe(user: any) { async updateMe(user: any, studentsOrder: number[] = []) {
const {role, uid, account, rid, video, audio, chat, boardId, linkId, sharedId, muteChat, muteAudio, grantBoard} = user; const {role, uid, account, rid, video, audio, chat, boardId, linkId, sharedId, muteChat, muteAudio, grantBoard} = user;
const key = role === 'teacher' ? 'teacher' : uid; const key = role === 'teacher' ? 'teacher' : uid;
const me = this.state.me; const me = this.state.me;
...@@ -1216,17 +1124,17 @@ export class RoomStore { ...@@ -1216,17 +1124,17 @@ export class RoomStore {
} }
const data: any = {}; const data: any = {};
data[key] = attrs; data[key] = attrs;
// if (studentsOrder && studentsOrder.length) { if (studentsOrder && studentsOrder.length) {
// data['studentsOrder'] = studentsOrder; data['studentsOrder'] = studentsOrder;
// } }
console.log('update me', data); console.log('update me', data);
await this.rtmClient.addOrUpdateChannelAttributes(data); await this.rtmClient.addOrUpdateChannelAttributes(data);
// let res = await this.rtmClient.updateChannelAttrsByKey(key, attrs); // let res = await this.rtmClient.updateChannelAttrsByKey(key, attrs);
// return res; // return res;
} }
async syncStageStudent(accounts: any[], studentsOrder: number[]) { async syncStageStudent__(accounts: any[], studentsOrder: number[]) {
debugger // debugger
let needUpdate =false; let needUpdate =false;
const data: any = {}; const data: any = {};
const accData: any = {}; const accData: any = {};
...@@ -1236,14 +1144,14 @@ export class RoomStore { ...@@ -1236,14 +1144,14 @@ export class RoomStore {
} }
}) })
// 学生在台上并且退出了
const accIds: any[] = Object.keys(accData).map(uid => +uid); const accIds: any[] = Object.keys(accData).map(uid => +uid);
if (!accIds.length) { if (!accIds.length) {
return return
} }
// 学生在台上并且退出了
const exitUserIds = studentsOrder.filter( uid => { const exitUserIds = studentsOrder.filter( uid => {
return !accIds.includes(uid); return !accIds.includes(uid) && uid != 0;
}); });
if (exitUserIds.length) { if (exitUserIds.length) {
exitUserIds.forEach( uid => { exitUserIds.forEach( uid => {
...@@ -1281,7 +1189,7 @@ export class RoomStore { ...@@ -1281,7 +1189,7 @@ export class RoomStore {
} }
if (studentsOrder.length < STAGE_NUM) { if (studentsOrder.length < STAGE_NUM) {
const newUid = accIds.find( uid => { const newUid = accIds.find( uid => {
return !studentsOrder.includes(+uid); return !studentsOrder.includes(+uid) ;
}); });
const newStudents = accData[newUid]; const newStudents = accData[newUid];
if (newStudents) { if (newStudents) {
...@@ -1297,6 +1205,122 @@ export class RoomStore { ...@@ -1297,6 +1205,122 @@ export class RoomStore {
} }
} }
} }
for (const uid of Object.keys(accData)) {
if (!studentsOrder.includes(+uid)) {
const ud = {...accData[uid]};
ud.video = 0;
ud.audio = 0;
data[uid] = ud
}
}
if (needUpdate) {
data['studentsOrder'] = studentsOrder;
// await this.rtmClient.addOrUpdateChannelAttributes(data);
return data;
}
}
async syncStageStudent(accounts: any[], studentsOrder: number[]) {
// debugger
let needUpdate =false;
const data: any = {};
const accData: any = {};
accounts.forEach(acc => {
if (acc.role == 'student') {
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
}
})
}
if (studentsOrder.length != STAGE_NUM) {
if (studentsOrder.length > STAGE_NUM) {
//台上队列尾部挤下去的人音视频关闭
const lostIdx: number[] = [];
[...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;
// }
}
}
} else {
// 台上有空闲
const emptyIndex = studentsOrder.indexOf(0);
if (emptyIndex > -1) {
//和小于台上人数补进去一样
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;
// }
}
}
}
for (const uid of Object.keys(accData)) {
if (!studentsOrder.includes(+uid)) {
const ud = {...accData[uid]};
ud.video = 0;
ud.audio = 1;
data[uid] = ud
}
}
if (needUpdate) { if (needUpdate) {
data['studentsOrder'] = studentsOrder; data['studentsOrder'] = studentsOrder;
// await this.rtmClient.addOrUpdateChannelAttributes(data); // await this.rtmClient.addOrUpdateChannelAttributes(data);
...@@ -1306,8 +1330,8 @@ export class RoomStore { ...@@ -1306,8 +1330,8 @@ export class RoomStore {
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 users = 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, {
role: it.role, role: it.role,
account: it.account, account: it.account,
...@@ -1341,7 +1365,15 @@ export class RoomStore { ...@@ -1341,7 +1365,15 @@ export class RoomStore {
}) })
} }
*/ */
console.log('users 4', 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;
if (users.get(me.uid)) { if (users.get(me.uid)) {
...@@ -1364,13 +1396,13 @@ export class RoomStore { ...@@ -1364,13 +1396,13 @@ export class RoomStore {
lockBoard: room.lock_board, lockBoard: room.lock_board,
}) })
//教师维护学生顺序,仅保留STAGE_NUM个,并更新到频道属性 //教师维护学生顺序,仅保留STAGE_NUM个,并更新到频道属性
const newInfo: any = await this.syncStageStudent(JSON.parse(JSON.stringify(accounts)), [...studentsOrder]); /*const newInfo: any = await this.syncStageStudent(JSON.parse(JSON.stringify(accounts)), [...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);
} }*/
} }
const newClassState: ClassState = {} as ClassState; const newClassState: ClassState = {} as ClassState;
...@@ -1431,16 +1463,45 @@ export class RoomStore { ...@@ -1431,16 +1463,45 @@ export class RoomStore {
async exitAll() { async exitAll() {
console.log('exitAll') console.log('exitAll')
if (this.state.me.role == 'teacher') {
await this.rtmClient.clearChannelAttributes()
}
try { try {
await this.rtmClient.exit(); if (this.state.me.role == 'teacher') {
await this.rtcClient.exit(); await this.rtmClient.clearChannelAttributes()
} else {
await this.rtmClient.clearChannelAttributesByKeys([`${this.state.me.uid}`]);
}
await this.rtmClient.logout();
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");
}).catch(console.warn)
.finally(() => {
roomStore.removeLocalStream();
});
// await this.rtcClient.exit();
GlobalStorage.clear('agora_room');
} catch(err) { } catch(err) {
console.warn(err); // debugger
console.warn('exitAll', err);
} finally { } finally {
GlobalStorage.clear('agora_room');
this.state = {} as RoomState; this.state = {} as RoomState;
this.state = { this.state = {
...this.defaultState ...this.defaultState
......
...@@ -98,9 +98,12 @@ export default class AgoraRTMClient { ...@@ -98,9 +98,12 @@ export default class AgoraRTMClient {
this._logged = true; this._logged = true;
return return
} }
async clearChannelAttributesByKeys(uids: string[]) {
await this._client.deleteChannelAttributesByKeys(this._currentChannelName, uids, {enableNotificationToChannelMembers: true});
}
async clearChannelAttributes() { async clearChannelAttributes() {
await this._client.clearChannelAttributes(this._currentChannelName); await this._client.clearChannelAttributes(this._currentChannelName, {enableNotificationToChannelMembers: true});
} }
async logout () { async logout () {
...@@ -155,7 +158,11 @@ export default class AgoraRTMClient { ...@@ -155,7 +158,11 @@ export default class AgoraRTMClient {
this.destroyChannel(channel); this.destroyChannel(channel);
} }
} }
async leaveChannel() {
// await this._client.clearLocalUserAttributes();
await this.leave(this._currentChannelName);
await this.logout();
}
async exit() { async exit() {
await this.deleteChannelAttributesByKey(); await this.deleteChannelAttributesByKey();
await this.leave(this._currentChannelName); await this.leave(this._currentChannelName);
......
export const STAGE_NUM: number = 1; export const STAGE_NUM: number = 6;
...@@ -256,6 +256,7 @@ export function resolveChannelAttrs(json: object) { ...@@ -256,6 +256,7 @@ export function resolveChannelAttrs(json: object) {
let students: any[] = []; let students: any[] = [];
for (let key of Object.keys(json)) { for (let key of Object.keys(json)) {
if (key === 'teacher') continue; if (key === 'teacher') continue;
if (!/^[0-9]+$/.test(key)) continue;
const student = jsonParse(_.get(json, `${key}.value`)); const student = jsonParse(_.get(json, `${key}.value`));
if (student && Object.keys(student).length) { if (student && Object.keys(student).length) {
student.uid = key; student.uid = key;
......
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