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
3a2d2dba
Commit
3a2d2dba
authored
Feb 11, 2020
by
liujiaxin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加iframe和白板切换
parent
4e8f74c3
Changes
16
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
376 additions
and
235 deletions
+376
-235
courseware-frame.tsx
src/components/courseware-frame.tsx
+1
-2
mediaboard.tsx
src/components/mediaboard.tsx
+15
-15
member-holder.tsx
src/components/member-holder.tsx
+1
-1
roomboard.tsx
src/components/roomboard.tsx
+4
-4
student-list.scss
src/components/student-list.scss
+6
-5
student-list.tsx
src/components/student-list.tsx
+3
-3
tools-switcher-controller.tsx
src/components/tools-switcher-controller.tsx
+54
-0
video-stage-list.tsx
src/components/video-stage-list.tsx
+3
-3
control.tsx
src/components/whiteboard/control.tsx
+12
-12
use-streams.ts
src/hooks/use-streams.ts
+39
-73
index.tsx
src/pages/classroom/index.tsx
+15
-9
small-class.tsx
src/pages/classroom/small-class.tsx
+9
-10
room.ts
src/stores/room.ts
+169
-27
agora-rtc-client.ts
src/utils/agora-rtc-client.ts
+10
-10
agora-rtm-client.ts
src/utils/agora-rtm-client.ts
+18
-7
helper.ts
src/utils/helper.ts
+17
-54
No files found.
src/components/courseware-frame.tsx
View file @
3a2d2dba
...
...
@@ -25,7 +25,7 @@ const useStyles = makeStyles({
flex
:
1
},
style4
:
{
height
:
3
0
,
height
:
3
6
,
width
:
100
,
flex
:
0
},
...
...
@@ -39,7 +39,6 @@ const CourseWareFrame: React.FC = () => {
const
ref
=
useRef
<
any
>
(
''
);
const
[
src
,
setSrc
]
=
useState
(
''
)
const
changSrc
=
(
evt
:
any
)
=>
{
debugger
setSrc
(
ref
.
current
.
value
);
}
...
...
src/components/mediaboard.tsx
View file @
3a2d2dba
...
...
@@ -23,6 +23,7 @@ import AgoraWebClient, { SHARE_ID } from '../utils/agora-rtc-client';
import
"
white-web-sdk/style/index.css
"
;
import
{
ViewMode
}
from
'
white-web-sdk
'
;
const
pathName
=
(
path
:
string
):
string
=>
{
const
reg
=
/
\/([^/]
*
)\/
/g
;
reg
.
exec
(
path
);
...
...
@@ -46,13 +47,13 @@ const MediaBoard: React.FC<MediaBoardProps> = ({
const
roomState
=
useRoomState
();
const
whiteboardState
=
useWhiteboardState
();
const
role
=
roomState
.
me
.
role
;
const
room
=
whiteboardState
.
room
;
const
me
=
roomState
.
me
;
const
course
=
roomState
.
course
;
const
rtmState
=
roomState
.
rtm
;
const
ref
=
useRef
<
any
>
(
false
);
const
[
pageTool
,
setPageTool
]
=
useState
<
string
>
(
''
);
...
...
@@ -184,7 +185,7 @@ const MediaBoard: React.FC<MediaBoardProps> = ({
return
+
roomStore
.
state
.
me
.
uid
===
+
roomStore
.
state
.
course
.
linkId
;
},
[
roomStore
.
state
.
me
.
uid
,
roomStore
.
state
.
course
.
linkId
]);
const
location
=
useLocation
();
const
current
=
useMemo
(()
=>
{
...
...
@@ -309,7 +310,7 @@ const items = [
},
[
me
.
role
,
me
.
grantBoard
,
location
]);
const
[
tool
,
setTool
]
=
useState
<
string
|
any
>
(
drawable
===
'
drawable
'
?
'
pencil
'
:
''
);
const
[
selector
,
updateSelector
]
=
useState
<
string
>
(
''
);
const
handleToolClick
=
(
evt
:
any
,
name
:
string
)
=>
{
...
...
@@ -417,7 +418,7 @@ const items = [
const
UploadPanel
=
useCallback
(()
=>
{
if
(
tool
!==
'
upload
'
||
!
room
)
return
null
;
return
(<
UploadBtn
return
(<
UploadBtn
room=
{
room
}
uuid=
{
room
.
uuid
}
roomToken=
{
room
.
roomToken
}
...
...
@@ -505,7 +506,7 @@ const items = [
return
(
<
Progress
title=
{
"
uploading...
"
}
/>
)
}
else
}
else
if
(
convertPhase
===
'
converting
'
)
{
return
(
<
Progress
title=
{
"
converting...
"
}
/>
...
...
@@ -524,10 +525,10 @@ const items = [
b
:
color
[
2
],
}
}
return
(
<
div
className=
{
`media-board ${drawable}`
}
>
{
sharedStream
?
{
sharedStream
?
<
VideoPlayer
id=
{
`${sharedStream.streamID}`
}
domId=
{
`shared-${sharedStream.streamID}`
}
...
...
@@ -545,7 +546,7 @@ const items = [
/>
}
<
div
className=
"layer"
>
{
!
sharedStream
?
{
!
sharedStream
?
<>
<
Tools
items=
{
toolItems
}
...
...
@@ -560,7 +561,7 @@ const items = [
<
UploadPanel
/>
{
children
?
children
:
null
}
</
div
>
{
me
.
role
===
'
teacher
'
&&
room
?
{
/*
me.role === 'teacher' && room ?
<ScaleController
zoomScale={scale}
onClick={() => {
...
...
@@ -577,8 +578,7 @@ const items = [
}}
/>
:
null
}
null*/
}
{
showControl
?
<
Control
notice=
{
globalState
.
notice
}
...
...
@@ -589,7 +589,7 @@ const items = [
totalPage=
{
totalPage
}
isHost=
{
isHost
}
onClick=
{
handlePageTool
}
/>
:
null
}
{
tool
===
'
folder
'
&&
whiteboardState
.
room
?
{
tool
===
'
folder
'
&&
whiteboardState
.
room
?
<
ResourcesMenu
active=
{
whiteboardState
.
activeDir
}
items=
{
whiteboardState
.
dirs
}
...
...
@@ -609,6 +609,6 @@ const items = [
<
UploadProgressView
/>
</
div
>
)
}
}
export
default
React
.
memo
(
MediaBoard
);
\ No newline at end of file
export
default
React
.
memo
(
MediaBoard
);
src/components/member-holder.tsx
View file @
3a2d2dba
...
...
@@ -234,7 +234,7 @@ const MemberHolder: React.FC<MemberHolderProps> = ({
{
me
.
uid
===
id
||
me
.
role
===
'
teacher
'
?
<
span
className=
"media-btn"
>
<
Link
component=
"button"
onClick=
{
onAudioClick
}
>
{
audio
?
"
静音
"
:
"
开麦
"
}
</
Link
>
<
Link
component=
"button"
onClick=
{
onStageUp
}
>
上台
</
Link
>
{
me
.
role
===
'
teacher
'
?
<
Link
component=
"button"
onClick=
{
onStageUp
}
>
上台
</
Link
>
:
null
}
{
/*<Icon onClick={onAudioClick} className={audio ? "icon-speaker-on" : "icon-speaker-off"} data={"audio"} />*/
}
{
/*<Icon onClick={onVideoClick} className={video ? "icons-camera-unmute-s" : "icons-camera-mute-s"} data={"video"} />*/
}
</
span
>
:
null
}
...
...
src/components/roomboard.tsx
View file @
3a2d2dba
...
...
@@ -9,7 +9,7 @@ export default function Roomboard (props: any) {
messages
,
sendMessage
,
handleChange
}
=
useChatText
();
const
[
active
,
setActive
]
=
useState
(
'
chatroom
'
);
const
[
active
,
setActive
]
=
useState
(
'
studentList
'
);
const
[
visible
,
setVisible
]
=
useState
(
true
);
const
toggleCollapse
=
(
evt
:
any
)
=>
{
...
...
@@ -19,10 +19,10 @@ export default function Roomboard (props: any) {
return
(
<>
<
div
className=
{
`${visible ? "icon-collapse-off" : "icon-collapse-on" } fixed`
}
onClick=
{
toggleCollapse
}
></
div
>
{
visible
?
{
visible
?
<
div
className=
{
`small-class chat-board`
}
>
<
div
className=
"menu"
>
<
div
onClick=
{
()
=>
{
setActive
(
'
chatroom
'
)
}
}
className=
{
`item ${active === 'chatroom' ? 'active' : ''}`
}
>
Chatroom
</
div
>
{
/*<div onClick={() => { setActive('chatroom') }} className={`item ${active === 'chatroom' ? 'active' : ''}`}>Chatroom</div>*/
}
<
div
onClick=
{
()
=>
{
setActive
(
'
studentList
'
)
}
}
className=
{
`item ${active === 'studentList' ? 'active' : ''}`
}
>
Student List
</
div
>
</
div
>
{
...
...
@@ -42,4 +42,4 @@ export default function Roomboard (props: any) {
:
null
}
</>
)
}
\ No newline at end of file
}
src/components/student-list.scss
View file @
3a2d2dba
...
...
@@ -22,13 +22,14 @@
font-weight
:
400
;
color
:rgba
(
3
,
3
,
3
,
1
)
;
line-height
:
17px
;
max-width
:
113px
;
min-width
:
113px
;
//max-width: 113px;
//min-width: 113px;
flex
:
1
;
}
.attrs-group
{
display
:
flex
;
min-width
:
148px
;
max-width
:
148px
;
//
min-width: 148px;
//
max-width: 148px;
justify-content
:
space-evenly
;
.items
{
margin
:
4px
;
...
...
@@ -43,4 +44,4 @@
}
}
}
}
\ No newline at end of file
}
src/components/student-list.tsx
View file @
3a2d2dba
...
...
@@ -89,8 +89,8 @@ export default function StudentList ({
<
div
key=
{
key
}
className=
"item"
>
<
div
className=
"nickname"
>
{
item
.
account
}
</
div
>
<
div
className=
"attrs-group"
>
<
CustomIcon
type=
"grantBoard"
id=
{
item
.
uid
}
value=
{
Boolean
(
item
.
grantBoard
)
}
icon=
"connect"
onClick=
{
handleClick
}
/>
<
CustomIcon
type=
"chat"
id=
{
item
.
uid
}
value=
{
Boolean
(
item
.
chat
)
}
icon=
"chat"
onClick=
{
handleClick
}
/>
{
/*<CustomIcon type="grantBoard" id={item.uid} value={Boolean(item.grantBoard)} icon="connect" onClick={handleClick} />*/
}
{
/*<CustomIcon type="chat" id={item.uid} value={Boolean(item.chat)} icon="chat" onClick={handleClick} />*/
}
<
CustomIcon
type=
"audio"
id=
{
item
.
uid
}
value=
{
Boolean
(
item
.
audio
)
}
icon=
"audio"
onClick=
{
handleClick
}
/>
<
CustomIcon
type=
"video"
id=
{
item
.
uid
}
value=
{
Boolean
(
item
.
video
)
}
icon=
"video"
onClick=
{
handleClick
}
/>
</
div
>
...
...
@@ -98,4 +98,4 @@ export default function StudentList ({
))
}
</
div
>
)
}
\ No newline at end of file
}
src/components/tools-switcher-controller.tsx
0 → 100644
View file @
3a2d2dba
import
React
from
'
react
'
;
import
BuildIcon
from
'
@material-ui/icons/Build
'
;
import
{
makeStyles
}
from
"
@material-ui/core/styles
"
;
import
{
Link
}
from
"
react-router-dom
"
;
export
type
ToolsSwitcherControllerProps
=
{
// zoomScale: number
// zoomChange: (scale: number) => void
onClick
:
()
=>
void
// onClickBoardLock: () => void
};
const
toolsStyle
=
makeStyles
({
button
:
{
width
:
'
42px!important
'
,
lineHeight
:
42
,
cursor
:
'
pointer
'
,
userSelect
:
'
none
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
boxSizing
:
'
border-box
'
,
position
:
'
absolute
'
,
left
:
10
,
bottom
:
10
,
height
:
42
,
background
:
'
#fff
'
,
boxShadow
:
'
0 2px 4px 0 rgba(0,0,0,.1)
'
,
borderRadius
:
6
,
border
:
'
1px solid #dbe2e5
'
,
zIndex
:
9
,
},
icon
:
{
margin
:
'
0 auto
'
}
});
export
const
ToolsSwitcherController
:
React
.
FC
<
ToolsSwitcherControllerProps
>
=
({
onClick
})
=>
{
const
classes
=
toolsStyle
();
return
(
<
div
className=
{
`tool-switcher-controls ${classes.button}`
}
onClick=
{
()
=>
onClick
()
}
>
<
BuildIcon
className=
{
classes
.
icon
}
></
BuildIcon
>
{
/*<div className="zoom-icon" onClick={() => this.props.onClick()}>*/
}
{
/*</div>*/
}
{
/*<div className="zoom-hold"></div>*/
}
{
/*<div className="zoom-size">{Math.ceil(this.props.zoomScale * 100)} %</div>*/
}
{
/*<div className="zoom-items">*/
}
{
/* <div className="item zoom-in" onClick={() => this.moveRuleIndex(-1)}>-</div>*/
}
{
/* <div className="item zoom-out" onClick={() => this.moveRuleIndex(+1)}>+</div>*/
}
{
/*</div>*/
}
{
/*<div className="lock-board" onClick={() => this.props.onClickBoardLock() }></div>*/
}
</
div
>
);
}
src/components/video-stage-list.tsx
View file @
3a2d2dba
...
...
@@ -11,8 +11,8 @@ import MemberHolder from "./member-holder";
function
VideoShowList
()
{
const
{
teacher
,
stageStudents
,
onPlayerClick
}
=
useStream
();
const
{
teacher
,
students
,
onPlayerClick
}
=
useStream
();
...
...
@@ -65,7 +65,7 @@ function VideoShowList() {
// return <div className="camera">
// <div key=
{
`stage${i}`
}
className
=
"
agora-video-view camera-holder
"
>
{
i
}<
/
div
>
//
</
div
>
;
const
student
=
st
ageStudents
.
stage
&&
stageStudents
.
stage
[
i
];
const
student
=
st
udents
&&
students
.
slice
(
0
,
STAGE_NUM
)
[
i
];
return
<
div
className=
"camera"
key=
{
`stage_video${i}`
}
>
{
student
?
<
VideoPlayer
...
...
@@ -122,7 +122,7 @@ function VideoShowList() {
)*/
}
{
st
ageStudents
.
wait
?
stageStudents
.
wait
.
map
((
student
:
AgoraMediaStream
,
key
:
number
)
=>
(
{
st
udents
?
students
.
slice
(
STAGE_NUM
)
.
map
((
student
:
AgoraMediaStream
,
key
:
number
)
=>
(
// <VideoPlayer
// role="student"
// domId=
{
`dom-${student.streamID}`
}
...
...
src/components/whiteboard/control.tsx
View file @
3a2d2dba
...
...
@@ -76,13 +76,13 @@ export default function Control({
handleRecording
(
evt
,
type
)
.
then
(()
=>
{}).
catch
(
console
.
warn
);
}
const
handleRecording
=
async
(
evt
:
any
,
type
:
string
)
=>
{
try
{
const
roomState
=
roomStore
.
state
;
const
me
=
roomState
.
me
;
if
(
lock
.
current
||
!
me
.
uid
)
return
;
if
(
whiteboard
.
state
.
recording
)
{
if
(
!
canStop
())
return
;
let
mediaUrl
=
await
whiteboard
.
stopRecording
();
...
...
@@ -90,7 +90,7 @@ export default function Control({
type
:
'
recording
'
,
message
:
'
stop recording success
'
,
});
if
(
whiteboard
.
state
.
endTime
if
(
whiteboard
.
state
.
endTime
&&
whiteboard
.
state
.
startTime
)
{
const
{
endTime
,
startTime
,
roomUUID
}
=
whiteboard
.
clearRecording
();
await
roomStore
.
rtmClient
.
sendChannelMessage
(
JSON
.
stringify
({
...
...
@@ -128,14 +128,14 @@ export default function Control({
return
(
<
div
className=
"controls-container"
>
<
div
className=
"interactive"
>
{
notice
?
{
notice
?
<
ControlItem
name=
{
notice
.
reason
}
onClick=
{
onClick
}
active=
{
notice
.
reason
===
current
}
/>
:
null
}
</
div
>
<
div
className=
"controls"
>
{
!
sharing
&&
role
===
'
teacher
'
?
{
/*
!sharing && role === 'teacher' ?
<>
<ControlItem name={`first_page`}
active={'first_page' === current}
...
...
@@ -154,14 +154,14 @@ export default function Control({
onClick={onClick} />
<div className="menu-split" style={{ marginLeft: '7px', marginRight: '7px' }}></div>
</> : null
}
*/
}
{
role
===
'
teacher
'
?
<>
<
ControlItem
name=
{
whiteboard
.
state
.
recording
?
'
stop_recording
'
:
'
recording
'
}
onClick=
{
onRecordButtonClick
}
active=
{
false
}
/>
{
/*<ControlItem*/
}
{
/*name={whiteboard.state.recording ? 'stop_recording' : 'recording'}*/
}
{
/*onClick={onRecordButtonClick}*/
}
{
/*active={false}*/
}
{
/*/>*/
}
<
ControlItem
name=
{
sharing
?
'
quit_screen_sharing
'
:
'
screen_sharing
'
}
onClick=
{
onClick
}
...
...
@@ -183,4 +183,4 @@ export default function Control({
</
div
>
)
}
\ No newline at end of file
}
src/hooks/use-streams.ts
View file @
3a2d2dba
...
...
@@ -7,8 +7,7 @@ import {STAGE_NUM} from "../utils/consts";
type
StreamValue
=
{
teacher
:
any
students
?:
any
[]
stageStudents
:
{
stage
:
any
[],
wait
:
any
[]}
students
:
any
[]
sharedStream
:
any
currentHost
:
any
onPlayerClick
:
(
type
:
string
,
streamID
:
number
,
uid
:
string
)
=>
Promise
<
any
>
...
...
@@ -61,7 +60,7 @@ export default function useStream () {
roomState
.
rtc
.
remoteStreams
,
roomState
.
rtc
.
localStream
]);
/*
const students__ = useMemo(() => {
const userAttrs = roomStore.state.users;
if (!me.uid || userAttrs.count() === 0) return [];
...
...
@@ -125,33 +124,44 @@ export default function useStream () {
roomState.rtc.remoteStreams,
roomState.rtc.localStream
]);
const
students_old
=
useMemo
(()
=>
{
*/
const
students
=
useMemo
(()
=>
{
const
userAttrs
=
roomStore
.
state
.
users
;
if
(
!
me
.
uid
||
userAttrs
.
count
()
===
0
)
return
[];
const
teacherUid
=
course
.
teacherId
;
const
peerUsers
=
roomState
.
rtc
.
users
;
// exclude teacher and me
let
studentIds
=
peerUsers
.
filter
((
it
:
number
)
=>
it
!==
+
teacherUid
&&
it
!==
+
me
.
uid
&&
it
!==
SHARE_ID
);
studentIds
=
studentIds
.
add
(
+
me
.
uid
);
const
studentStreams
:
any
[]
=
[];
const
myAttr
=
userAttrs
.
get
(
me
.
uid
);
const
studentsOrder
=
roomStore
.
state
.
studentsOrder
||
[];
console
.
log
(
100000
,
studentsOrder
);
console
.
log
(
11111111
,
studentIds
.
toArray
());
studentIds
=
studentIds
.
sort
(
function
(
a
,
b
){
return
studentsOrder
.
indexOf
(
+
a
)
-
studentsOrder
.
indexOf
(
+
b
);
});
console
.
log
(
222222
,
studentIds
.
toArray
());
// when i m student
if
(
me
.
role
===
'
student
'
)
{
if
(
myAttr
&&
roomState
.
rtc
.
localStream
)
{
const
_tmpStream
=
{
...
roomState
.
rtc
.
localStream
,
account
:
myAttr
.
account
,
video
:
myAttr
.
video
,
audio
:
myAttr
.
audio
,
local
:
true
,
}
studentStreams
.
push
(
_tmpStream
);
}
}
// capture all remote streams
for
(
let
studentId
of
studentIds
)
{
if
(
me
.
role
===
'
student
'
&&
+
me
.
uid
==
studentId
)
{
if
(
myAttr
&&
roomState
.
rtc
.
localStream
)
{
const
_tmpStream
=
{
...
roomState
.
rtc
.
localStream
,
account
:
myAttr
.
account
,
video
:
myAttr
.
video
,
audio
:
myAttr
.
audio
,
local
:
true
,
}
studentStreams
.
push
(
_tmpStream
);
continue
}
}
const
studentAttr
=
userAttrs
.
get
(
''
+
studentId
);
const
stream
=
roomState
.
rtc
.
remoteStreams
.
get
(
+
studentId
);
if
(
studentAttr
)
{
...
...
@@ -173,6 +183,7 @@ export default function useStream () {
studentStreams
.
push
(
_tmpStream
);
}
}
console
.
log
(
'
studentStreams
'
,
studentStreams
);
return
studentStreams
;
},
[
course
,
...
...
@@ -182,6 +193,7 @@ export default function useStream () {
roomState
.
rtc
.
localStream
]);
/*
const stageStudents: { wait: any[]; stage: any[] } = useMemo(() => {
const userAttrs = roomStore.state.users;
if (!me.uid || userAttrs.count() === 0) return { wait: [], stage:[] };
...
...
@@ -191,7 +203,8 @@ export default function useStream () {
let studentIds = peerUsers.filter((it: number) => it !== +teacherUid && it !== +me.uid && it !== SHARE_ID);
const studentStreams: any[] = [];
userAttrs.forEach(u => {
if
(
me
.
role
===
'
student
'
&&
`
${
u
.
uid
}
`
===
`
${
me
.
uid
}
`
)
{
const uid = u.uid;
if (me.role === 'student' && `${uid}` === `${me.uid}`) {
if (roomState.rtc.localStream) {
const _tmpStream = {
...roomState.rtc.localStream,
...
...
@@ -203,11 +216,11 @@ export default function useStream () {
studentStreams.push(_tmpStream);
}
} else {
const
studentAttr
=
userAttrs
.
get
(
`
${
u
.
u
id
}
`
);
const
stream
=
roomState
.
rtc
.
remoteStreams
.
get
(
+
u
.
u
id
);
const studentAttr = userAttrs.get(`${uid}`);
const stream = roomState.rtc.remoteStreams.get(+uid);
if (studentAttr) {
let _tmpStream = {
streamID
:
u
.
u
id
,
streamID: uid,
account: studentAttr.account,
video: studentAttr.video,
audio: studentAttr.audio,
...
...
@@ -215,7 +228,7 @@ export default function useStream () {
if (stream) {
_tmpStream = {
...stream,
streamID
:
u
.
u
id
,
streamID: uid,
account: studentAttr.account,
video: studentAttr.video,
audio: studentAttr.audio,
...
...
@@ -225,55 +238,6 @@ export default function useStream () {
}
}
})
/*
const myAttr = userAttrs.get(me.uid);
const ss = [];
// when i m student
if (me.role === 'student') {
if (myAttr && roomState.rtc.localStream) {
const _tmpStream = {
...roomState.rtc.localStream,
account: myAttr.account,
video: myAttr.video,
audio: myAttr.audio,
local: true,
}
studentStreams.push(_tmpStream);
ss.push(me.uid)
}
}
for (let studentId of studentIds) {
ss.push(studentId)
}
console.log('studentIds', ss.join(', '))
// capture all remote streams
for (let studentId of studentIds) {
const studentAttr = userAttrs.get(''+studentId);
const stream = roomState.rtc.remoteStreams.get(+studentId);
if (studentAttr) {
let _tmpStream = {
streamID: studentId,
account: studentAttr.account,
video: studentAttr.video,
audio: studentAttr.audio,
}
if (stream) {
_tmpStream = {
...stream,
streamID: studentId,
account: studentAttr.account,
video: studentAttr.video,
audio: studentAttr.audio,
}
}
studentStreams.push(_tmpStream);
}
}*/
console.log('studentStreams', studentStreams)
return {
stage: [...studentStreams.slice(0,STAGE_NUM)],
...
...
@@ -286,6 +250,8 @@ export default function useStream () {
roomState.rtc.remoteStreams,
roomState.rtc.localStream
]);
*/
const
sharedStream
=
useMemo
(()
=>
{
const
sharedUid
=
SHARE_ID
;
...
...
@@ -358,8 +324,8 @@ export default function useStream () {
const
value
:
StreamValue
=
{
teacher
:
teacher
,
//
students: students,
stageStudents
,
students
:
students
,
//
stageStudents,
sharedStream
:
sharedStream
,
currentHost
:
currentHost
,
onPlayerClick
:
async
(
type
:
string
,
streamID
:
number
,
uid
:
string
)
=>
{
...
...
src/pages/classroom/index.tsx
View file @
3a2d2dba
...
...
@@ -93,7 +93,7 @@ export function RoomPage({ children }: any) {
const
classroom
=
Boolean
(
location
.
pathname
.
match
(
/classroom/
));
const
isBigClass
=
Boolean
(
location
.
pathname
.
match
(
/big-class/
));
const
isSmallClass
=
Boolean
(
location
.
pathname
.
match
(
/small-class/
));
const
prevRoute
=
useRef
<
string
>
(
location
.
pathname
);
useEffect
(()
=>
{
console
.
log
(
"
[route] prevRoute:
"
,
prevRoute
.
current
);
...
...
@@ -107,12 +107,12 @@ export function RoomPage({ children }: any) {
});
}
},
[
location
]);
const
rtc
=
useRef
<
boolean
>
(
false
);
const
canPublish
=
useMemo
(()
=>
{
return
!
isBigClass
||
(
isBigClass
&&
(
isBigClass
&&
(
me
.
role
===
'
teacher
'
||
+
me
.
uid
===
+
course
.
linkId
));
},
[
me
.
uid
,
course
.
linkId
,
me
.
role
,
isBigClass
]);
...
...
@@ -141,6 +141,7 @@ export function RoomPage({ children }: any) {
if
(
platform
===
'
web
'
)
{
const
webClient
=
rtcClient
as
AgoraWebClient
;
if
(
!
webClient
.
published
)
return
;
console
.
log
(
'
unpublish
'
,
2
);
webClient
.
unpublishLocalStream
()
.
then
(()
=>
{
...
...
@@ -181,7 +182,7 @@ export function RoomPage({ children }: any) {
webClient
.
publishLocalStream
(
streamSpec
)
.
then
(()
=>
{
console
.
log
(
"
[agora-web] publish local stream
"
);
console
.
log
(
"
[agora-web] publish local stream
"
,
roomStore
.
state
.
me
.
uid
);
}).
catch
(
console
.
warn
)
.
finally
(()
=>
{
publishLock
.
current
=
false
;
...
...
@@ -215,13 +216,13 @@ export function RoomPage({ children }: any) {
}
console
.
log
(
"
[agora-rtc] add event listener
"
);
webClient
.
rtc
.
on
(
'
onTokenPrivilegeWillExpire
'
,
(
evt
:
any
)
=>
{
// you need obtain the `newToken` token from server side
// you need obtain the `newToken` token from server side
const
newToken
=
''
;
webClient
.
rtc
.
renewToken
(
newToken
);
console
.
log
(
'
[agora-web] onTokenPrivilegeWillExpire
'
,
evt
);
});
webClient
.
rtc
.
on
(
'
onTokenPrivilegeDidExpire
'
,
(
evt
:
any
)
=>
{
// you need obtain the `newToken` token from server side
// you need obtain the `newToken` token from server side
const
newToken
=
''
;
webClient
.
rtc
.
renewToken
(
newToken
);
console
.
log
(
'
[agora-web] onTokenPrivilegeDidExpire
'
,
evt
);
...
...
@@ -234,6 +235,7 @@ export function RoomPage({ children }: any) {
roomStore
.
addLocalStream
(
_stream
);
});
webClient
.
rtc
.
on
(
'
stream-subscribed
'
,
({
stream
}:
any
)
=>
{
console
.
log
(
'
stream
'
,
2
,
'
stream-subscribed
'
)
const
streamID
=
stream
.
getId
();
// when streamID is not share_id use switch high or low stream in dual stream mode
if
(
location
.
pathname
.
match
(
/small-class/
)
&&
streamID
!==
SHARE_ID
)
{
...
...
@@ -247,11 +249,13 @@ export function RoomPage({ children }: any) {
console
.
log
(
"
[agora-web] dual stream set low for student
"
);
}
}
console
.
log
(
'
stream
'
,
3
,
'
addRemoteStream to play
'
)
const
_stream
=
new
AgoraStream
(
stream
,
stream
.
getId
(),
false
);
console
.
log
(
"
[agora-web] subscribe remote stream, id:
"
,
stream
.
getId
());
roomStore
.
addRemoteStream
(
_stream
);
});
webClient
.
rtc
.
on
(
'
stream-added
'
,
({
stream
}:
any
)
=>
{
console
.
log
(
'
stream
'
,
1
,
'
stream-added
'
)
console
.
log
(
"
[agora-web] added remote stream, id:
"
,
stream
.
getId
());
webClient
.
subscribe
(
stream
);
});
...
...
@@ -291,12 +295,12 @@ export function RoomPage({ children }: any) {
// WARN: IF YOU ENABLED APP CERTIFICATE, PLEASE SIGN YOUR TOKEN IN YOUR SERVER SIDE AND OBTAIN IT FROM YOUR OWN TRUSTED SERVER API
webClient
.
joinChannel
({
uid
:
+
roomState
.
me
.
uid
,
uid
:
+
roomState
.
me
.
uid
,
channel
:
roomState
.
course
.
rid
,
token
:
''
,
dual
:
isSmallClass
}).
then
(()
=>
{
}).
catch
(
console
.
warn
).
finally
(()
=>
{
rtc
.
current
=
false
;
});
...
...
@@ -316,6 +320,8 @@ export function RoomPage({ children }: any) {
for
(
let
eventName
of
events
)
{
webClient
.
rtc
.
off
(
eventName
,
()
=>
{});
}
console
.
log
(
"
[agora-web] rtmClient logout
"
);
roomStore
.
rtmClient
.
logout
();
console
.
log
(
"
[agora-web] remove event listener
"
);
!
rtc
.
current
&&
webClient
.
exit
().
then
(()
=>
{
console
.
log
(
"
[agora-web] do remove event listener
"
);
...
...
@@ -379,7 +385,7 @@ export function RoomPage({ children }: any) {
});
// WARN: IF YOU ENABLED APP CERTIFICATE, PLEASE SIGN YOUR TOKEN IN YOUR SERVER SIDE AND OBTAIN IT FROM YOUR OWN TRUSTED SERVER API
nativeClient
.
joinChannel
({
uid
:
+
roomState
.
me
.
uid
,
uid
:
+
roomState
.
me
.
uid
,
channel
:
roomState
.
course
.
rid
,
token
:
''
,
dual
:
isSmallClass
...
...
src/pages/classroom/small-class.tsx
View file @
3a2d2dba
import
React
,
{
useLayoutEffect
,
use
Ref
}
from
'
react
'
;
import
React
,
{
useLayoutEffect
,
use
Memo
,
useRef
,
useState
}
from
'
react
'
;
// import VideoMarquee from '../../components/video-marquee';
//
import MediaBoard from '../../components/mediaboard';
import
MediaBoard
from
'
../../components/mediaboard
'
;
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
"
;
export
default
function
SmallClass
()
{
// let search = window.location.search;
// let params = new URLSearchParams(search);
// let src = params.get('src');
// console.log(9999, src)
// useLayoutEffect(() => {
// }, [src]);
const
[
tool
,
setTool
]
=
useState
(
true
);
return
(
<
div
className=
"room-container"
>
{
/*<VideoMarquee />*/
}
<
VideoShowList
/>
<
div
className=
"container"
>
{
/*<MediaBoard />*/
}
<
CourseWareFrame
></
CourseWareFrame
>
<
ToolsSwitcherController
onClick=
{
()
=>
{
setTool
(
!
tool
);
}
}
/>
{
tool
?
<
MediaBoard
/>
:
<
CourseWareFrame
></
CourseWareFrame
>
}
<
Roomboard
currentActive=
{
'
media
'
}
/>
</
div
>
</
div
>
...
...
src/stores/room.ts
View file @
3a2d2dba
This diff is collapsed.
Click to expand it.
src/utils/agora-rtc-client.ts
View file @
3a2d2dba
...
...
@@ -20,7 +20,7 @@ export interface AgoraStreamSpec {
}
const
streamEvents
:
string
[]
=
[
"
accessAllowed
"
,
"
accessAllowed
"
,
"
accessDenied
"
,
"
stopScreenSharing
"
,
"
videoTrackEnded
"
,
...
...
@@ -104,7 +104,7 @@ class AgoraRTCClient {
if
(
evtName
===
"
peer-leave
"
)
{
console
.
log
(
"
[agora-web] peer-leave:
"
,
args
);
}
this
.
_bus
.
emit
(
evtName
,
args
);
});
}
...
...
@@ -155,7 +155,7 @@ class AgoraRTCClient {
this
.
_bus
.
off
(
evtName
,
cb
);
}
async
publish
()
{
async
publish
()
{
console
.
log
(
'
rtc publish
'
)
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
this
.
_published
)
{
return
resolve
();
...
...
@@ -170,7 +170,7 @@ class AgoraRTCClient {
})
}
async
unpublish
()
{
async
unpublish
()
{
console
.
log
(
'
unpublish
'
,
1
);
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
this
.
_published
||
!
this
.
_localStream
)
{
return
resolve
();
...
...
@@ -234,7 +234,7 @@ class AgoraRTCClient {
})
}
async
leave
()
{
async
leave
()
{
console
.
log
(
9999
,
'
rtc leave
'
);
if
(
this
.
_client
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
this
.
_client
.
leave
(
resolve
,
reject
);
...
...
@@ -341,7 +341,7 @@ export default class AgoraWebClient {
roomStore
.
setRTCJoined
(
true
);
}
async
leaveChannel
()
{
async
leaveChannel
()
{
console
.
log
(
'
unpublish leaveChannel
'
,
3
);
this
.
localUid
=
0
;
this
.
channel
=
''
;
await
this
.
unpublishLocalStream
();
...
...
@@ -357,17 +357,17 @@ export default class AgoraWebClient {
}
async
publishLocalStream
(
data
:
AgoraStreamSpec
)
{
console
.
log
(
"
publish local stream
"
,
this
.
publishe
d
);
console
.
log
(
"
publish local stream
"
,
roomStore
.
state
.
me
.
ui
d
);
if
(
this
.
published
)
{
await
this
.
unpublishLocalStream
();
console
.
log
(
"
[agora-web] unpublished
"
,
this
.
publishe
d
);
console
.
log
(
"
[agora-web] unpublished
"
,
roomStore
.
state
.
me
.
ui
d
);
}
await
this
.
rtc
.
createLocalStream
(
data
);
await
this
.
rtc
.
publish
();
this
.
published
=
true
;
}
async
unpublishLocalStream
()
{
async
unpublishLocalStream
()
{
console
.
log
(
'
unpublish LocalStream
'
,
4
);
console
.
log
(
"
[agora-web] invoke unpublishStream
"
);
await
this
.
rtc
.
unpublish
();
this
.
published
=
false
;
...
...
@@ -443,4 +443,4 @@ export default class AgoraWebClient {
setRemoteVideoStreamType
(
stream
:
any
,
type
:
number
)
{
this
.
rtc
.
setRemoteVideoStreamType
(
stream
,
type
);
}
}
\ No newline at end of file
}
src/utils/agora-rtm-client.ts
View file @
3a2d2dba
...
...
@@ -54,8 +54,8 @@ export default class AgoraRTMClient {
this
.
_channelAttrsKey
=
null
;
this
.
_client
=
AgoraRTM
.
createInstance
(
APP_ID
,
{
enableLogUpload
:
false
,
logFilter
});
}
async
addOrUpdateChannelAttributes
(
data
:
any
):
Promise
<
string
>
{
return
await
this
.
_client
.
addOrUpdateChannelAttributes
(
this
.
_currentChannelName
,
data
,
{
enableNotificationToChannelMembers
:
true
});
async
addOrUpdateChannelAttributes
(
data
:
any
,
enableNotificationToChannelMembers
=
true
):
Promise
<
string
>
{
return
await
this
.
_client
.
addOrUpdateChannelAttributes
(
this
.
_currentChannelName
,
data
,
{
enableNotificationToChannelMembers
});
}
public
removeAllListeners
():
any
{
...
...
@@ -83,6 +83,7 @@ export default class AgoraRTMClient {
}
async
login
(
uid
:
string
,
token
?:
string
)
{
this
.
_channelAttrsKey
=
uid
;
await
this
.
_client
.
login
({
uid
,
token
});
this
.
_client
.
on
(
"
ConnectionStateChanged
"
,
(
newState
:
string
,
reason
:
string
)
=>
{
this
.
_bus
.
emit
(
"
ConnectionStateChanged
"
,
{
newState
,
reason
});
...
...
@@ -96,6 +97,7 @@ export default class AgoraRTMClient {
async
logout
()
{
if
(
!
this
.
_logged
)
return
;
await
this
.
leave
(
this
.
_currentChannelName
);
await
this
.
_client
.
logout
();
this
.
destroy
();
this
.
_logged
=
false
;
...
...
@@ -155,6 +157,9 @@ export default class AgoraRTMClient {
async
sendChannelMessage
(
msg
:
string
)
{
return
this
.
_currentChannel
.
sendMessage
({
text
:
msg
});
}
async
setChannelAttrsKey
(
key
:
any
)
{
this
.
_channelAttrsKey
=
`
${
key
}
`
;
}
async
updateChannelAttrsByKey
(
data
:
{[
key
:
string
]:
string
}
/*key: string, attrs: any*/
)
{
// this._channelAttrsKey = key;
const
channelAttributes
:
{[
key
:
string
]:
string
}
=
{};
...
...
@@ -217,6 +222,7 @@ export default class AgoraRTMClient {
const
teacher
=
accounts
.
find
((
it
:
any
)
=>
it
.
role
===
'
teacher
'
);
if
(
teacher
&&
results
[
teacher
.
uid
])
{
results
.
teacher
=
results
[
teacher
.
uid
];
results
.
teacherId
=
teacher
.
uid
;
results
.
teacherCount
=
1
;
}
results
.
totalCount
=
accounts
.
filter
((
it
:
any
)
=>
results
[
it
.
uid
]).
length
;
...
...
@@ -240,11 +246,16 @@ export default class AgoraRTMClient {
}
continue
;
}
const
student
=
jsonParse
(
_
.
get
(
json
,
`
${
key
}
.value`
));
// when student is not empty object
if
(
student
&&
Object
.
keys
(
student
).
length
)
{
student
.
uid
=
key
;
accounts
.
push
(
student
);
// if (key === 'studentsOrder') {
// continue
// }
if
(
/^
[
0-9
]
+$/
.
test
(
key
))
{
const
student
=
jsonParse
(
_
.
get
(
json
,
`
${
key
}
.value`
));
// when student is not empty object
if
(
student
&&
Object
.
keys
(
student
).
length
)
{
student
.
uid
=
key
;
accounts
.
push
(
student
);
}
}
}
return
accounts
;
...
...
src/utils/helper.ts
View file @
3a2d2dba
...
...
@@ -226,6 +226,7 @@ export function resolveChannelAttrs(json: object) {
let
studentsOrder
:
number
[]
=
[];
if
((
json
as
any
).
studentsOrder
&&
(
json
as
any
).
studentsOrder
.
value
)
{
studentsOrder
=
JSON
.
parse
((
json
as
any
).
studentsOrder
.
value
)
||
[];
studentsOrder
=
studentsOrder
.
map
(
id
=>
+
id
)
}
delete
(
json
as
any
).
studentsOrder
;
...
...
@@ -249,52 +250,32 @@ export function resolveChannelAttrs(json: object) {
}
}
}
const
students
=
[];
let
students
:
any
[]
=
[];
for
(
let
key
of
Object
.
keys
(
json
))
{
if
(
key
===
'
teacher
'
)
continue
;
const
student
=
jsonParse
(
_
.
get
(
json
,
`
${
key
}
.value`
));
if
(
student
&&
Object
.
keys
(
student
).
length
)
{
student
.
uid
=
key
;
student
.
role
=
'
student
'
;
students
.
push
(
student
);
}
}
console
.
log
(
'
resolveChannelAttrs 1
'
,
students
);
console
.
log
(
'
resolveChannelAttrs 2
'
,
studentsOrder
);
/*
const studentsMeta = [];
for (let key of Object.keys(json)) {
if (key === 'teacher') continue;
// if (!/^[0-9]+$/.test(key)) continue;
// const student = jsonParse(_.get(json, `${key}.value`));
// @ts-ignore
const studentMeta = json[key];
studentMeta.key = key;
studentsMeta.push(studentMeta)
// if (student && Object.keys(student).length) {
// student.uid = key;
// students.push(student);
// }
}
studentsMeta.sort((a, b)=>{
return a.lastUpdateTs - b.lastUpdateTs
})
let i = 0;
for (const s of studentsMeta) {
// const studentMeta = jsonParse(s);
const student = jsonParse(_.get(json, `${s.key}.value`));
if (student && Object.keys(student).length) {
student.uid = s.key;
// student.posIdx = i;
students.push(student);
i++;
}
}*/
// console.log('resolveChannelAttrs', studentsMeta);
// const orderedStudents: any[] = [];
// students.sort(function(a, b){
// return studentsOrder.indexOf(+a.uid) - studentsOrder.indexOf(+b.uid);
// });
// console.log('resolveChannelAttrs 1', students);
// console.log('resolveChannelAttrs 2', studentsOrder);
const
accounts
=
[];
const
accounts
:
any
[]
=
[];
if
(
teacherJson
&&
Object
.
keys
(
teacherJson
).
length
)
{
accounts
.
push
({
role
:
'
teacher
'
,
...
...
@@ -324,33 +305,15 @@ export function resolveChannelAttrs(json: object) {
grant_board
:
+
student
.
grant_board
,
});
}
// console.log('resolveChannelAttrs 3', accounts);
if
(
studentsOrder
.
length
==
0
)
{
accounts
.
forEach
(
a
=>
{
studentsOrder
.
push
(
+
a
.
uid
);
})
}
else
{
const
notInOrder
=
[];
accounts
.
forEach
(
a
=>
{
if
(
!
studentsOrder
.
includes
(
+
a
.
uid
))
{
notInOrder
.
push
(
+
a
.
uid
)
}
})
const
aUids
=
accounts
.
map
(
a
=>
{
return
+
a
.
uid
;
});
const
a
=
new
Set
(
studentsOrder
);
const
b
=
new
Set
(
aUids
);
}
return
{
teacher
:
teacherJson
,
students
:
students
,
accounts
,
room
,
studentsOrder
};
}
...
...
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