Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
live
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liujiaxin
live
Commits
6236ec53
Commit
6236ec53
authored
Feb 15, 2020
by
liujiaxin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
台上位置固定6人。下台为空闲状态
parent
da303d2d
Changes
20
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
617 additions
and
324 deletions
+617
-324
package.json
package.json
+1
-1
courseware-frame.tsx
src/components/courseware-frame.tsx
+2
-2
room.tsx
src/components/dialog/room.tsx
+12
-6
member-holder.tsx
src/components/member-holder.tsx
+2
-1
nav.tsx
src/components/nav.tsx
+13
-8
room-tools-bar.scss
src/components/room-tools-bar.scss
+12
-26
room-tools-bar.tsx
src/components/room-tools-bar.tsx
+47
-2
video-player.scss
src/components/video-player.scss
+24
-1
video-player.tsx
src/components/video-player.tsx
+44
-9
root-container.tsx
src/containers/root-container.tsx
+12
-1
use-streams.ts
src/hooks/use-streams.ts
+60
-9
index.scss
src/index.scss
+4
-4
index.tsx
src/pages/classroom/index.tsx
+5
-4
small-class.tsx
src/pages/classroom/small-class.tsx
+9
-9
home.tsx
src/pages/home.tsx
+58
-3
agora-end-points.ts
src/services/agora-end-points.ts
+5
-0
room.ts
src/stores/room.ts
+296
-235
agora-rtm-client.ts
src/utils/agora-rtm-client.ts
+9
-2
consts.ts
src/utils/consts.ts
+1
-1
helper.ts
src/utils/helper.ts
+1
-0
No files found.
package.json
View file @
6236ec53
...
...
@@ -109,7 +109,7 @@
"electron:copy:web"
:
"cross-env REACT_APP_RUNTIME_PLATFORM=electron react-app-rewired build"
,
"electron:copy:electron"
:
"cpx ./app/**/*.js ./build"
,
"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"
,
"test"
:
"react-app-rewired test --env=jsdom"
,
"analyze"
:
"source-map-explorer 'build/static/js/*.js'"
,
...
...
src/components/courseware-frame.tsx
View file @
6236ec53
...
...
@@ -37,7 +37,7 @@ const useStyles = makeStyles({
});
const
CourseWareFrame
:
React
.
FC
=
()
=>
{
const
classes
=
useStyles
();
// `${ENDPOINT}/airclass_ol?token=${this.userToken}`;
const
src
=
endPoint
.
getCourseUrl
();
return
(
<
div
className=
{
classes
.
style1
}
>
...
...
src/components/dialog/room.tsx
View file @
6236ec53
...
...
@@ -83,8 +83,14 @@ const DialogContainer = () => {
const
onConfirm
=
(
type
:
string
)
=>
{
if
(
type
===
'
exitRoom
'
)
{
// debugger
globalStore
.
showLoading
();
roomStore
.
exitAll
().
then
(()
=>
{
globalStore
.
stopLoading
();
globalStore
.
removeDialog
();
history
.
push
(
'
/
'
);
})
}
else
if
(
type
===
'
apply
'
)
{
Promise
.
all
([
...
...
src/components/member-holder.tsx
View file @
6236ec53
...
...
@@ -231,7 +231,8 @@ const MemberHolder: React.FC<MemberHolderProps> = ({
{
account
?
<
div
className=
"user-profile"
>
<
span
className=
"account"
>
{
account
}
</
span
>
{
me
.
uid
===
id
||
me
.
role
===
'
teacher
'
?
{
/*me.uid === id ||*/
}
{
me
.
role
===
'
teacher
'
?
<
span
className=
"media-btn"
>
<
Link
component=
"button"
onClick=
{
onAudioClick
}
>
{
audio
?
"
静音
"
:
"
开麦
"
}
</
Link
>
{
me
.
role
===
'
teacher
'
?
<
Link
component=
"button"
onClick=
{
onStageUp
}
>
上台
</
Link
>
:
null
}
...
...
src/components/nav.tsx
View file @
6236ec53
...
...
@@ -13,7 +13,9 @@ import { isElectron, platform } from '../utils/platform';
import
{
useRoomState
}
from
'
../containers/root-container
'
;
import
{
roomStore
}
from
'
../stores/room
'
;
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
{
delay
:
string
network
:
string
...
...
@@ -68,14 +70,17 @@ export function Nav ({
<
span
className=
"time"
>
{
moment
.
utc
(
time
).
format
(
'
HH:mm:ss
'
)
}
</
span
>
</
div
>
<
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) => {
handleClick("setting");
}}/> : null}
<Icon className="icon-exit" onClick={(evt: any) => {
handleClick("exit");
}} />
</
div
>
</div>
*/
}
<
NavBtn
/>
</
div
>
</
div
>
...
...
src/components/room-tools-bar.scss
View file @
6236ec53
.room-tool-box
{
right
:
10px
;
width
:
46px
;
border
:
1px
solid
#dbe2e5
;
bottom
:
10px
;
cursor
:
pointer
;
display
:
flex
;
z-index
:
9
;
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
);
flex-direction
:
row
;
.tool-btn
{
cursor
:
pointer
;
display
:
flex
;
height
:
3
8
px
;
width
:
3
8
px
;
margin
:
2
px
;
height
:
3
2
px
;
width
:
3
2
px
;
margin
:
4
px
;
border-radius
:
4px
;
background
:
#565656
;
//
background: #565656;
box-sizing
:
border-box
;
color
:
#eee
;
//
color: #eee;
svg
{
width
:
3
8
px
;
height
:
3
8
px
;
width
:
3
0
px
;
height
:
3
0
px
;
display
:
block
;
}
}
.tool-btn
:hover
{
background-color
:
rgba
(
0
,
0
,
0
,
0
.07
);
border-radius
:
4px
;
}
}
src/components/room-tools-bar.tsx
View file @
6236ec53
...
...
@@ -6,9 +6,38 @@ import { VolumeOff, VolumeUp, ImportExport } from '@material-ui/icons';
import
{
roomStore
}
from
"
../stores/room
"
;
import
{
useRoomState
}
from
"
../containers/root-container
"
;
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 const RoomToolsBar: React.FC<Props> = ({onClick}) => {
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
(()
=>
{
return
{
muteAudio
:
roomState
.
course
.
muteAudio
...
...
@@ -25,11 +54,27 @@ export default function RoomToolsBar (props: any) {
const
randomStageUp
=
async
()
=>
{
await
roomStore
.
randomStageUp
()
}
return
(
<>
<
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
>
</>
)
...
...
src/components/video-player.scss
View file @
6236ec53
...
...
@@ -18,7 +18,30 @@
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
{
z-index
:
3
;
width
:
100%
;
...
...
src/components/video-player.tsx
View file @
6236ec53
...
...
@@ -4,6 +4,21 @@ import './video-player.scss';
import
{
AgoraElectronStream
,
StreamType
,
nativeRTCClient
as
nativeClient
}
from
'
../utils/agora-electron-client
'
;
import
{
useRoomState
}
from
'
../containers/root-container
'
;
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
;
...
...
@@ -41,6 +56,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
handleClose
,
close
})
=>
{
const
style
=
useStyles
();
const
loadVideo
=
useRef
<
boolean
>
(
false
);
const
loadAudio
=
useRef
<
boolean
>
(
false
);
...
...
@@ -219,6 +235,12 @@ const onAudioClick = (evt: any) => {
handleClick
(
'
audio
'
,
streamID
,
id
);
}
}
const
onStageDownClick
=
(
evt
:
any
)
=>
{
if
(
handleClick
&&
id
)
{
handleClick
(
'
stageDown
'
,
streamID
,
id
);
}
}
const
onVideoClick
=
(
evt
:
any
)
=>
{
if
(
handleClick
&&
id
)
{
...
...
@@ -242,17 +264,30 @@ return (
account
?
<
div
className=
"video-profile"
>
<
span
className=
"account"
>
{
account
}
</
span
>
{
me
.
uid
===
id
||
me
.
role
===
'
teacher
'
?
<
span
className=
"media-btn"
>
<
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
"
}
/>
{
streamID
<=
0
?
null
:
<
div
>
{
me
.
uid
===
id
||
me
.
role
===
'
teacher
'
?
<
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
"
}
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
>
:
null
}
...
...
src/containers/root-container.tsx
View file @
6236ec53
...
...
@@ -5,6 +5,7 @@ import { WhiteboardState, whiteboard } from '../stores/whiteboard';
import
{
useHistory
,
useLocation
}
from
'
react-router-dom
'
;
import
{
resolveMessage
,
resolvePeerMessage
,
resolveChannelAttrs
,
jsonParse
}
from
'
../utils/helper
'
;
import
GlobalStorage
from
'
../utils/custom-storage
'
;
import
{
endPoint
}
from
"
../services/agora-end-points
"
;
export
type
IRootProvider
=
{
globalState
:
GlobalState
...
...
@@ -105,9 +106,17 @@ export const RootProvider: React.FC<any> = ({children}) => {
}).
catch
(
console
.
warn
);
});
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
);
// 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
)
});
rtmClient
.
on
(
"
MemberJoined
"
,
(
memberId
:
string
)
=>
{
...
...
@@ -152,6 +161,8 @@ export const RootProvider: React.FC<any> = ({children}) => {
me
:
room
.
me
,
course
:
room
.
course
,
mediaDevice
:
room
.
mediaDevice
,
cwLink
:
endPoint
.
userToken
,
studentsOrder
:
room
.
studentsOrder
});
// WARN: DEBUG ONLY MUST REMOVED IN PRODUCTION
//@ts-ignore
...
...
src/hooks/use-streams.ts
View file @
6236ec53
...
...
@@ -143,17 +143,67 @@ export default function useStream () {
console
.
log
(
100000
,
studentsOrder
);
console
.
log
(
11111111
,
studentIds
.
toArray
());
studentIds
=
studentIds
.
sort
(
function
(
a
,
b
){
if
(
studentsOrder
.
indexOf
(
+
a
)
<
0
)
{
return
0
}
if
(
studentsOrder
.
indexOf
(
+
b
)
<
0
)
{
return
0
}
return
studentsOrder
.
indexOf
(
+
a
)
-
studentsOrder
.
indexOf
(
+
b
);
});
console
.
log
(
222222
,
studentIds
.
toArray
());
// studentIds = studentIds.sort(function(a, b){
// if (studentsOrder.indexOf(+a) < 0) {return 0}
// if (studentsOrder.indexOf(+b) < 0) {return 0}
// return studentsOrder.indexOf(+a) - studentsOrder.indexOf(+b);
// });
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
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
(
myAttr
&&
roomState
.
rtc
.
localStream
)
{
console
.
log
(
'
use local stream
'
)
...
...
@@ -168,6 +218,7 @@ export default function useStream () {
continue
}
}
const
studentAttr
=
userAttrs
.
get
(
''
+
studentId
);
const
stream
=
roomState
.
rtc
.
remoteStreams
.
get
(
+
studentId
);
if
(
studentAttr
)
{
...
...
@@ -190,7 +241,7 @@ export default function useStream () {
}
}
userAttrs
.
forEach
((
v
,
k
)
=>
{
if
(
!
s
tudentI
ds
.
includes
(
+
v
.
uid
))
{
if
(
!
s
i
ds
.
includes
(
+
v
.
uid
))
{
if
(
`
${
v
.
uid
}
`
!=
`
${
teacherUid
}
`
)
{
const
s
:
any
=
{
streamID
:
+
v
.
uid
,
...
...
@@ -210,7 +261,7 @@ export default function useStream () {
},
[
course
,
me
.
uid
,
roomStore
.
state
.
studentsOrder
,
//
roomStore.state.studentsOrder,
roomState
.
rtc
.
users
,
roomState
.
rtc
.
remoteStreams
,
roomState
.
rtc
.
localStream
...
...
src/index.scss
View file @
6236ec53
...
...
@@ -288,13 +288,13 @@ body {
@media
screen
and
(
max-width
:
780px
)
{
.home-cover-web
{
min-width
:
780px
;
//
min-width: 780px;
}
}
@media
screen
and
(
max-height
:
768px
)
{
.home-cover-web
{
min-height
:
768px
;
//
min-height: 768px;
}
}
...
...
src/pages/classroom/index.tsx
View file @
6236ec53
...
...
@@ -213,7 +213,7 @@ export function RoomPage({ children }: any) {
]);
useEffect
(()
=>
{
debugger
//
debugger
if
(
!
roomState
.
me
.
uid
||
!
roomState
.
course
.
rid
)
return
;
if
(
classroom
)
{
if
(
platform
===
'
web
'
)
{
...
...
@@ -301,7 +301,7 @@ export function RoomPage({ children }: any) {
})
*/
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
console
.
info
(
1.1
,
'
rtc join
'
)
// if (roomState.me.role == 'teacher') {
...
...
@@ -311,7 +311,7 @@ export function RoomPage({ children }: any) {
uid
:
+
roomState
.
me
.
uid
,
channel
:
roomState
.
course
.
rid
,
token
:
''
,
dual
:
isSmallClass
dual
:
true
}).
then
(()
=>
{
console
.
info
(
2
,
'
rtc join ok
'
)
roomStore
.
publishMeStream
();
...
...
@@ -452,10 +452,11 @@ export function RoomPage({ children }: any) {
console
.
log
(
1.15
,
'
stagedown?
'
,
222222
);
roomStore
.
unPublishMeStream
();
}
console
.
log
(
1.2
,
studentsOrder
,
'
studentsOrder changed publish stream
'
);
}
console
.
log
(
1.2
,
studentsOrder
,
'
studentsOrder changed publish stream
'
);
},
[
studentsOrder
]);
...
...
src/pages/classroom/small-class.tsx
View file @
6236ec53
import
React
,
{
useEffect
,
useLayoutEffect
,
useMemo
,
useRef
,
useState
}
from
'
react
'
;
// import VideoMarquee from '../../components/video-marquee';
import
MediaBoard
from
'
../../components/mediaboard
'
;
import
Roomboard
from
'
../../components/roomboard
'
;
//
import Roomboard from '../../components/roomboard';
import
'
./small-class.scss
'
;
import
CourseWareFrame
from
"
../../components/courseware-frame
"
;
import
VideoShowList
from
"
../../components/video-stage-list
"
;
import
{
ToolsSwitcherController
}
from
"
../../components/tools-switcher-controller
"
;
import
{
roomStore
}
from
"
../../stores/room
"
;
import
{
TOOL_TYPE
}
from
"
../../utils/types
"
;
import
{
platform
}
from
"
../../utils/platform
"
;
import
AgoraWebClient
from
"
../../utils/agora-rtc-client
"
;
import
{
AgoraElectronClient
}
from
"
../../utils/agora-electron-client
"
;
//
import {platform} from "../../utils/platform";
//
import AgoraWebClient from "../../utils/agora-rtc-client";
//
import {AgoraElectronClient} from "../../utils/agora-electron-client";
import
{
useRoomState
}
from
"
../../containers/root-container
"
;
import
RoomToolsBar
from
"
../../components/room-tools-bar
"
;
//
import RoomToolsBar from "../../components/room-tools-bar";
export
default
function
SmallClass
()
{
const
[
tool
,
setTool
]
=
useState
(
true
);
...
...
@@ -39,15 +39,15 @@ export default function SmallClass() {
{
/*<VideoMarquee />*/
}
<
VideoShowList
/>
<
div
className=
"container"
>
{
roomStore
.
state
.
me
.
role
==
'
teacher
'
?<
ToolsSwitcherController
onClick=
{
()
=>
{
changeTool
();
}
}
/>
:
null
}
{
/*{roomStore.state.me.role == 'teacher' ?<ToolsSwitcherController onClick={() => {*/
}
{
/* changeTool();*/
}
{
/*}}/> : null}*/
}
{
currentTool
==
TOOL_TYPE
.
WHITEBOARD
?
<
MediaBoard
/>
:
<
CourseWareFrame
></
CourseWareFrame
>
}
{
roomStore
.
state
.
me
.
role
==
'
teacher
'
?
<
RoomToolsBar
/>
:
null
}
{
/*{ roomStore.state.me.role == 'teacher' ? <Roomboard currentActive={'media'} /> : null}*/
}
</
div
>
</
div
>
...
...
src/pages/home.tsx
View file @
6236ec53
...
...
@@ -79,17 +79,70 @@ function HomePage() {
setRequired
({...
required
,
yourPass
:
'
missing your password
'
});
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
)
=>
{
console
.
log
(
userInfo
);
session
.
roomName
=
userInfo
[
"
class_id
"
];
session
.
yourName
=
userInfo
[
"
nick_name
"
];
session
.
roomName
=
userInfo
[
"
class_id
"
];
// userInfo["class_id"];
//
session.yourName = userInfo["nick_name"];
session
.
role
=
userInfo
[
"
classRole
"
]
===
"
tea
"
?
"
teacher
"
:
"
student
"
;
session
.
roomType
=
1
;
if
(
!
roomTypes
[
session
.
roomType
])
return
;
const
path
=
roomTypes
[
session
.
roomType
].
path
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)}`,
role
:
session
.
role
,
roomName
:
session
.
roomName
,
...
...
@@ -133,6 +186,8 @@ function HomePage() {
message
:
err
})
});
}
return
(
...
...
src/services/agora-end-points.ts
View file @
6236ec53
import
{
AgoraFetch
}
from
"
../utils/fetch
"
;
import
jwt_decode
from
'
jwt-decode
'
;
import
{
roomStore
}
from
"
../stores/room
"
;
const
ENDPOINT
:
string
=
process
.
env
.
REACT_APP_AGORA_ENDPOINT
as
string
;
...
...
@@ -78,6 +79,10 @@ export class EndPoint {
}
getCourseUrl
(){
if
(
!
this
.
userToken
)
{
this
.
userToken
=
roomStore
.
state
.
cwLink
;
}
console
.
log
(
'
getCourseUrl
'
,
this
.
userToken
)
return
`
${
ENDPOINT
}
/airclass_ol?token=
${
this
.
userToken
}
`
;
}
...
...
src/stores/room.ts
View file @
6236ec53
...
...
@@ -17,14 +17,14 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any,
permitted
:
true
,
reason
:
''
}
const
channelCountLimit
=
[
2
,
17
,
17
];
let
maximum
=
channelCountLimit
[
roomType
];
if
(
channelCount
>=
maximum
)
{
result
.
permitted
=
false
;
result
.
reason
=
'
The number of students and teacher have reached upper limit
'
;
return
result
;
}
//
const channelCountLimit = [2, 17, 17];
//
//
let maximum = channelCountLimit[roomType];
//
if (channelCount >= maximum) {
//
result.permitted = false;
//
result.reason = 'The number of students and teacher have reached upper limit';
//
return result;
//
}
const
teacher
=
get
(
onlineStatus
,
'
teacher
'
,
false
);
const
totalCount
:
number
=
get
(
onlineStatus
,
'
totalCount
'
,
0
);
...
...
@@ -38,13 +38,13 @@ function canJoin({uid, onlineStatus, roomType, channelCount, role}: { uid: any,
}
}
if
(
role
===
'
student
'
)
{
if
(
totalCount
+
1
>
maximum
)
{
result
.
permitted
=
false
;
result
.
reason
=
'
Student have reached upper limit
'
;
return
result
;
}
}
//
if (role === 'student') {
//
if (totalCount+1 > maximum) {
//
result.permitted = false;
//
result.reason = 'Student have reached upper limit';
//
return result;
//
}
//
}
return
result
;
}
...
...
@@ -124,6 +124,7 @@ export type RoomState = {
mediaDevice
:
MediaDeviceState
messages
:
List
<
ChatMessage
>
studentsOrder
:
number
[]
cwLink
:
string
,
}
export
type
AgoraMediaStream
=
{
...
...
@@ -289,212 +290,92 @@ export class RoomStore {
// @ts-ignore
window
[
'
roomStore
'
]
=
this
;
}
/*
async stageUpRtc(uid: string) {
let store = this._state.rtc.users;
let itemsList = store.toList();
const idx = itemsList.indexOf(+uid);
if (idx < STAGE_NUM) {
return;
}
itemsList = itemsList.insert(0, +uid);
const downVal = itemsList.get(STAGE_NUM);
if (downVal != undefined) {
itemsList = itemsList.splice(STAGE_NUM, 1).splice(idx, 1, downVal);
store = OrderedSet(itemsList)
} else {
return ;
}
this.state = {
...this.state,
rtc: {
...this.state.rtc,
users: store,
async
stageDown
(
uid
:
string
)
{
// debugger
//把台上位置变零
let
studentsOrder
:
any
[]
=
[...
this
.
state
.
studentsOrder
];
const
uidIndex
=
studentsOrder
.
indexOf
(
+
uid
)
if
(
uidIndex
>
-
1
)
{
studentsOrder
.
splice
(
uidIndex
,
1
,
0
);
const
data
:
any
=
{};
data
[
'
studentsOrder
'
]
=
studentsOrder
;
const
downUser
:
AgoraUser
|
undefined
=
this
.
state
.
users
.
get
(
`
${
uid
}
`
);
if
(
downUser
)
{
downUser
.
audio
=
0
;
downUser
.
video
=
0
;
data
[
uid
]
=
downUser
}
await
this
.
rtmClient
.
addOrUpdateChannelAttributes
(
data
);
}
this.commit(this.state);
}
async stageDownRtc(uid: string) {
let store = this._state.rtc.users;
console.log(store);
// let uid = 2;
// stageNum = 3;
let itemsList = store.toList();
let idx = itemsList.indexOf(+uid);
if (idx >= STAGE_NUM) {
return;
async
stageUp
(
uid
:
string
)
{
let
studentsOrder
:
any
[]
=
[...
this
.
state
.
studentsOrder
];
const
data
:
any
=
{};
const
emptyIndex
=
studentsOrder
.
indexOf
(
0
);
if
(
emptyIndex
>
-
1
)
{
studentsOrder
[
emptyIndex
]
=
+
uid
;
const
upUser
:
AgoraUser
|
undefined
=
this
.
state
.
users
.
get
(
`
${
uid
}
`
);
if
(
upUser
)
{
upUser
.
video
=
1
;
upUser
.
audio
=
1
;
data
[
upUser
.
uid
]
=
upUser
;
}
let upVal = itemsList.get(STAGE_NUM);
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
{
return
}
this.state = {
...this.state,
rtc: {
...this.state.rtc,
users: store,
}
}
this.commit(this.state);
data
[
'
studentsOrder
'
]
=
studentsOrder
;
await
this
.
rtmClient
.
addOrUpdateChannelAttributes
(
data
);
}
async stageUp1(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 = JSON.parse(attrs)
const target = jsonParse(get(onlineStatus[uid], 'value'));
// 上台需要开启视频
// if (!+target.video) {
target.video = 1;
data[uid] = JSON.stringify(target)
// }
async
stageUp3333
(
uid
:
string
)
{
debugger
let
studentsOrder
:
any
[]
=
[...
this
.
state
.
studentsOrder
];
const tmp: [string, AgoraUser][] =[];
this._state.users.forEach((v, k)=>{
if (/^[0-9]+$/.test(k)) {
tmp.push([k, v])
const
data
:
any
=
{};
const
emptyIndex
=
studentsOrder
.
indexOf
(
0
);
if
(
emptyIndex
>
-
1
)
{
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 = {
// ...this.state,
// users
// }
// this.commit(this.state);
const
upUser
:
AgoraUser
|
undefined
=
this
.
state
.
users
.
get
(
`
${
uid
}
`
);
if
(
upUser
)
{
upUser
.
video
=
1
;
upUser
.
audio
=
1
;
data
[
upUser
.
uid
]
=
upUser
;
}
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);
if
(
!
lastUser
||
!
upUser
)
{
return
}
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][] =[];
this._state.users.forEach((v, k)=>
{
if (/^[0-9]+$/.test(k)) {
tmp.push([k, v])
if
(
studentsOrder
.
length
>
STAGE_NUM
)
{
studentsOrder
.
length
=
STAGE_NUM
}
}
})
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
;
await
this
.
rtmClient
.
addOrUpdateChannelAttributes
(
data
);
...
...
@@ -511,6 +392,7 @@ export class RoomStore {
// this.commit(this.state);
}
async
randomStageUp
()
{
const
users
=
roomStore
.
state
.
users
;
const
studentsOrder
=
roomStore
.
state
.
studentsOrder
||
[];
...
...
@@ -925,8 +807,16 @@ export class RoomStore {
const
channelMemberCount
=
await
this
.
rtmClient
.
getChannelMemberCount
([
rid
]);
const
channelCount
=
channelMemberCount
[
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
;
const
onlineStatus
=
await
this
.
rtmClient
.
queryOnlineStatusBy
(
accounts
);
/*
const me = accounts.find((o)=>{return `${payload.uid}` === `${o.uid}`});
if (onlineStatus[payload.uid]) {
...
...
@@ -962,9 +852,23 @@ export class RoomStore {
};
let
result
=
pass
===
false
?
canJoin
(
argsJoin
)
:
{
permitted
:
true
,
reason
:
''
};
let
studentsOrder
:
number
[]
=
[];
let
studentsOrder
:
number
[]
=
channelMeta
.
studentsOrder
||
[];
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) {
accounts.forEach(a => {
...
...
@@ -998,17 +902,21 @@ export class RoomStore {
}
}
*/
console
.
log
(
'
login accounts
'
,
accounts
)
let
onlineMe
=
accounts
.
find
((
it
:
any
)
=>
`
${
it
.
uid
}
`
==
`
${
payload
.
uid
}
`
);
if
(
onlineMe
)
{
payload
.
video
=
onlineMe
.
video
;
payload
.
audio
=
onlineMe
.
audio
;
}
else
{
if
(
channelMeta
.
studentsOrder
&&
channelMeta
.
studentsOrder
.
includes
(
+
payload
.
uid
))
{
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
=>
{
this
.
state
=
{
...
...
@@ -1020,7 +928,7 @@ export class RoomStore {
}
const
grantBoard
=
role
===
'
teacher
'
?
1
:
0
;
console
.
log
(
"
>>>>>>>>>>#room:
"
,
grantBoard
);
await
this
.
updateMe
({...
payload
,
grantBoard
});
await
this
.
updateMe
({...
payload
,
grantBoard
}
,
studentsOrder
);
this
.
commit
(
this
.
state
);
});
// this.state = {
...
...
@@ -1163,7 +1071,7 @@ export class RoomStore {
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
key
=
role
===
'
teacher
'
?
'
teacher
'
:
uid
;
const
me
=
this
.
state
.
me
;
...
...
@@ -1216,17 +1124,17 @@ export class RoomStore {
}
const
data
:
any
=
{};
data
[
key
]
=
attrs
;
//
if (studentsOrder && studentsOrder.length) {
//
data['studentsOrder'] = studentsOrder;
//
}
if
(
studentsOrder
&&
studentsOrder
.
length
)
{
data
[
'
studentsOrder
'
]
=
studentsOrder
;
}
console
.
log
(
'
update me
'
,
data
);
await
this
.
rtmClient
.
addOrUpdateChannelAttributes
(
data
);
// let res = await this.rtmClient.updateChannelAttrsByKey(key, attrs);
// return res;
}
async
syncStageStudent
(
accounts
:
any
[],
studentsOrder
:
number
[])
{
debugger
async
syncStageStudent
__
(
accounts
:
any
[],
studentsOrder
:
number
[])
{
//
debugger
let
needUpdate
=
false
;
const
data
:
any
=
{};
const
accData
:
any
=
{};
...
...
@@ -1236,14 +1144,14 @@ export class RoomStore {
}
})
// 学生在台上并且退出了
const
accIds
:
any
[]
=
Object
.
keys
(
accData
).
map
(
uid
=>
+
uid
);
if
(
!
accIds
.
length
)
{
return
}
// 学生在台上并且退出了
const
exitUserIds
=
studentsOrder
.
filter
(
uid
=>
{
return
!
accIds
.
includes
(
uid
);
return
!
accIds
.
includes
(
uid
)
&&
uid
!=
0
;
});
if
(
exitUserIds
.
length
)
{
exitUserIds
.
forEach
(
uid
=>
{
...
...
@@ -1281,7 +1189,93 @@ export class RoomStore {
}
if
(
studentsOrder
.
length
<
STAGE_NUM
)
{
const
newUid
=
accIds
.
find
(
uid
=>
{
return
!
studentsOrder
.
includes
(
+
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
(
+
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
)
{
...
...
@@ -1296,6 +1290,36 @@ export class RoomStore {
// }
}
}
}
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
)
{
data
[
'
studentsOrder
'
]
=
studentsOrder
;
...
...
@@ -1306,8 +1330,8 @@ export class RoomStore {
async
updateRoomAttrs
({
teacher
,
accounts
,
room
,
studentsOrder
}:
{
teacher
:
any
,
accounts
:
any
,
room
:
any
,
studentsOrder
:
number
[]})
{
console
.
log
(
"
[agora-board], room:
"
,
teacher
,
accounts
,
room
,
studentsOrder
);
const
users
=
accounts
.
reduce
((
acc
:
OrderedMap
<
string
,
AgoraUser
>
,
it
:
any
)
=>
{
const
exitUserIds
=
[];
const
users
:
OrderedMap
<
string
,
AgoraUser
>
=
accounts
.
reduce
((
acc
:
OrderedMap
<
string
,
AgoraUser
>
,
it
:
any
)
=>
{
return
acc
.
set
(
it
.
uid
,
{
role
:
it
.
role
,
account
:
it
.
account
,
...
...
@@ -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
;
if
(
users
.
get
(
me
.
uid
))
{
...
...
@@ -1364,13 +1396,13 @@ export class RoomStore {
lockBoard
:
room
.
lock_board
,
})
//教师维护学生顺序,仅保留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)
debugger
//
debugger
if (newInfo) {
newStudentsOrder = newInfo['studentsOrder'];
await this.rtmClient.addOrUpdateChannelAttributes(newInfo);
}
}
*/
}
const
newClassState
:
ClassState
=
{}
as
ClassState
;
...
...
@@ -1431,16 +1463,45 @@ export class RoomStore {
async
exitAll
()
{
console
.
log
(
'
exitAll
'
)
try
{
if
(
this
.
state
.
me
.
role
==
'
teacher
'
)
{
await
this
.
rtmClient
.
clearChannelAttributes
()
}
else
{
await
this
.
rtmClient
.
clearChannelAttributesByKeys
([
`
${
this
.
state
.
me
.
uid
}
`
]);
}
try
{
await
this
.
rtmClient
.
exit
();
await
this
.
rtcClient
.
exit
();
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
)
{
console
.
warn
(
err
);
// debugger
console
.
warn
(
'
exitAll
'
,
err
);
}
finally
{
GlobalStorage
.
clear
(
'
agora_room
'
);
this
.
state
=
{}
as
RoomState
;
this
.
state
=
{
...
this
.
defaultState
...
...
src/utils/agora-rtm-client.ts
View file @
6236ec53
...
...
@@ -98,9 +98,12 @@ export default class AgoraRTMClient {
this
.
_logged
=
true
;
return
}
async
clearChannelAttributesByKeys
(
uids
:
string
[])
{
await
this
.
_client
.
deleteChannelAttributesByKeys
(
this
.
_currentChannelName
,
uids
,
{
enableNotificationToChannelMembers
:
true
});
}
async
clearChannelAttributes
()
{
await
this
.
_client
.
clearChannelAttributes
(
this
.
_currentChannelName
);
await
this
.
_client
.
clearChannelAttributes
(
this
.
_currentChannelName
,
{
enableNotificationToChannelMembers
:
true
}
);
}
async
logout
()
{
...
...
@@ -155,7 +158,11 @@ export default class AgoraRTMClient {
this
.
destroyChannel
(
channel
);
}
}
async
leaveChannel
()
{
// await this._client.clearLocalUserAttributes();
await
this
.
leave
(
this
.
_currentChannelName
);
await
this
.
logout
();
}
async
exit
()
{
await
this
.
deleteChannelAttributesByKey
();
await
this
.
leave
(
this
.
_currentChannelName
);
...
...
src/utils/consts.ts
View file @
6236ec53
export
const
STAGE_NUM
:
number
=
1
;
export
const
STAGE_NUM
:
number
=
6
;
src/utils/helper.ts
View file @
6236ec53
...
...
@@ -256,6 +256,7 @@ export function resolveChannelAttrs(json: object) {
let
students
:
any
[]
=
[];
for
(
let
key
of
Object
.
keys
(
json
))
{
if
(
key
===
'
teacher
'
)
continue
;
if
(
!
/^
[
0-9
]
+$/
.
test
(
key
))
continue
;
const
student
=
jsonParse
(
_
.
get
(
json
,
`
${
key
}
.value`
));
if
(
student
&&
Object
.
keys
(
student
).
length
)
{
student
.
uid
=
key
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment