Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions template/customization-api/typeDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface VideoCallInterface extends BeforeAndAfterInterface {
captionPanel?: React.ComponentType;
transcriptPanel?: React.ComponentType;
virtualBackgroundPanel?: React.ComponentType<VBPanelProps>;
breakoutRoomPanel?: React.ComponentType;
customLayout?: (layouts: LayoutItem[]) => LayoutItem[];
wrapper?: React.ComponentType;
customAgentInterface?: React.ComponentType<CustomAgentInterfaceProps>;
Expand Down
1 change: 1 addition & 0 deletions template/defaultConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const DefaultConfig = {
ENABLE_WAITING_ROOM_AUTO_APPROVAL: false,
ENABLE_WAITING_ROOM_AUTO_REQUEST: false,
ENABLE_TEXT_TRACKS: false,
ENABLE_BREAKOUT_ROOM: false,
};

module.exports = DefaultConfig;
1 change: 1 addition & 0 deletions template/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ interface ConfigInterface {
ENABLE_WAITING_ROOM_AUTO_APPROVAL: boolean;
ENABLE_WAITING_ROOM_AUTO_REQUEST: boolean;
ENABLE_TEXT_TRACKS: boolean;
ENABLE_BREAKOUT_ROOM: boolean;
}
declare var $config: ConfigInterface;
declare module 'customization' {
Expand Down
19 changes: 19 additions & 0 deletions template/src/components/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ import {
toolbarItemVirtualBackgroundText,
toolbarItemWhiteboardText,
toolbarItemManageTextTracksText,
toolbarItemBreakoutRoomText,
} from '../language/default-labels/videoCallScreenLabels';
import {LogSource, logger} from '../logger/AppBuilderLogger';
import {useModal} from '../utils/useModal';
Expand Down Expand Up @@ -285,6 +286,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
const virtualBackgroundLabel = useString(toolbarItemVirtualBackgroundText)();
const chatLabel = useString(toolbarItemChatText)();
const inviteLabel = useString(toolbarItemInviteText)();
const breakoutRoomLabel = useString(toolbarItemBreakoutRoomText)();
const peopleLabel = useString(toolbarItemPeopleText)();
const layoutLabel = useString(toolbarItemLayoutText)();
const {dispatch} = useContext(DispatchContext);
Expand Down Expand Up @@ -834,6 +836,23 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
});
}

// 14. Breakout Room
const canAccessBreakoutRoom = useControlPermissionMatrix('breakoutRoom');
if (canAccessBreakoutRoom) {
actionMenuitems.push({
componentName: 'breakoutRoom',
order: 14,
icon: 'participants',
iconColor: $config.SECONDARY_ACTION_COLOR,
textColor: $config.FONT_COLOR,
title: breakoutRoomLabel,
onPress: () => {
setActionMenuVisible(false);
setSidePanel(SidePanelType.BreakoutRoom);
},
});
}

useEffect(() => {
if (isHovered) {
setActionMenuVisible(true);
Expand Down
253 changes: 253 additions & 0 deletions template/src/components/breakout-room/BreakoutRoomView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
********************************************
Copyright © 2021 Agora Lab, Inc., all rights reserved.
AppBuilder and all associated components, source code, APIs, services, and documentation
(the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
Use without a license or in violation of any license terms and conditions (including use for
any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
information visit https://appbuilder.agora.io.
*********************************************
*/
import React, {useContext, useState} from 'react';
import {
View,
StyleSheet,
ScrollView,
TouchableOpacity,
Text,
} from 'react-native';
import ParticipantSectionTitle from '../participants/ParticipantSectionTitle';
import AllHostParticipants from '../participants/AllHostParticipants';
import {useString} from '../../utils/useString';
import {isMobileUA, isWebInternal, useIsSmall} from '../../utils/common';
import ChatContext from '../ChatContext';
import CommonStyles from '../CommonStyles';
import {useLayout, useContent, TertiaryButton, Spacer} from 'customization-api';
import {getGridLayoutName} from '../../pages/video-call/DefaultLayouts';
import {BreakoutRoomHeader} from '../../pages/video-call/SidePanelHeader';
import useCaptionWidth from '../../../src/subComponents/caption/useCaptionWidth';
import {
peoplePanelInThisMeetingLabel,
peoplePanelNoUsersJoinedContent,
} from '../../../src/language/default-labels/videoCallScreenLabels';
import {useRoomInfo} from '../room-info/useRoomInfo';
import {BreakoutRoomInfo, useBreakoutRoomInfo} from './useBreakoutRoomInfo';

const BreakoutRoomGroupCard = ({name, participants}: BreakoutRoomInfo) => {
const {defaultContent} = useContent();
return (
<View
style={{
margin: 12,
}}>
<View
style={{
display: 'flex',
flexDirection: 'column',
gap: 8,
padding: 12,
borderRadius: 12,
borderWidth: 2,
borderColor: $config.CARD_LAYER_3_COLOR,
backgroundColor: $config.CARD_LAYER_1_COLOR,
}}>
<View
style={{
height: 24,
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Text style={{color: $config.FONT_COLOR}}>{name}</Text>
</View>
<View
style={{
display: 'flex',
flexDirection: 'column',
gap: 12,
alignSelf: 'stretch',
alignItems: 'flex-start',
}}>
{participants?.hosts?.length ? (
<>
<Text
style={{
color: $config.FONT_COLOR,
fontSize: 16,
fontWeight: '800',
}}>
Hosts
</Text>
{participants?.hosts?.map(uid => {
return (
<Text style={{color: $config.FONT_COLOR}}>
{defaultContent[uid].name}
</Text>
);
})}
</>
) : (
<></>
)}
<Spacer size={8} horizontal />
{participants?.attendees?.length ? (
<>
<Text
style={{
color: $config.FONT_COLOR,
fontSize: 16,
fontWeight: '800',
}}>
Attendees
</Text>
{participants?.attendees?.map(uid => {
return (
<Text style={{color: $config.FONT_COLOR}}>
{defaultContent[uid].name}
</Text>
);
})}
</>
) : (
<></>
)}
</View>
<View
style={{
display: 'flex',
flexDirection: 'column',
gap: 8,
alignSelf: 'flex-end',
backgroundColor: $config.CARD_LAYER_2_COLOR,
padding: 10,
borderRadius: 8,
}}>
<Text style={{color: $config.FONT_COLOR}}>
Members{' - '}
{participants?.hosts?.length + participants?.attendees?.length}
</Text>
</View>
</View>
</View>
);
};

const BreakoutRoomView = props => {
const {activeUids, customContent} = useContent();
const {onlineUsersCount} = useContext(ChatContext);
const {showHeader = true} = props;
const meetingParticpantsLabel = useString(peoplePanelInThisMeetingLabel)();
const noUsersJoinedYet = useString(peoplePanelNoUsersJoinedContent)();
const isSmall = useIsSmall();
const [showMeetingParticipants, setShowMeetingParticipants] = useState(true);
const {currentLayout} = useLayout();
const {transcriptHeight} = useCaptionWidth();
const {
data: {isHost},
} = useRoomInfo();

const {createBreakoutRoomGroup, breakoutRoomInfo, startBreakoutRoom} =
useBreakoutRoomInfo();

return (
<View
testID="videocall-breakout-room"
style={[
isMobileUA()
? //mobile and mobile web
CommonStyles.sidePanelContainerNative
: isSmall()
? // desktop minimized
CommonStyles.sidePanelContainerWebMinimzed
: // desktop maximized
CommonStyles.sidePanelContainerWeb,
isWebInternal() && !isSmall() && currentLayout === getGridLayoutName()
? {marginTop: 4}
: {},
//@ts-ignore
transcriptHeight && !isMobileUA() && {height: transcriptHeight},
]}>
{showHeader && <BreakoutRoomHeader />}
<ScrollView style={[style.bodyContainer]}>
<>
<ParticipantSectionTitle
title={meetingParticpantsLabel}
count={onlineUsersCount}
isOpen={showMeetingParticipants}
onPress={() => setShowMeetingParticipants(!showMeetingParticipants)}
/>
{showMeetingParticipants ? (
<AllHostParticipants
emptyMessage={noUsersJoinedYet}
//custom content shouldn't be shown in the participant list. so filtering the activeuids
uids={activeUids.filter(i => !customContent[i])}
isMobile={isSmall()}
updateActionSheet={props.updateActionSheet}
handleClose={props.handleClose}
hideControls={true}
showBreakoutRoomMenu={true}
from="breakout-room"
/>
) : (
<></>
)}
</>
<View
style={{
flex: 1,
justifyContent: 'center',
alignSelf: 'flex-end',
margin: 10,
}}>
<TouchableOpacity onPress={() => createBreakoutRoomGroup()}>
<Text style={{color: $config.PRIMARY_ACTION_BRAND_COLOR}}>
+ Create Group
</Text>
</TouchableOpacity>
</View>
{breakoutRoomInfo.map(props => {
return <BreakoutRoomGroupCard {...props} />;
})}
</ScrollView>
{isHost && (
<View style={style.footer}>
<View style={{display: 'flex', flex: 1}}>
<TertiaryButton onPress={() => {}} text={'CANCEL'} />
</View>
<Spacer size={16} horizontal />
<View style={{display: 'flex', flex: 1}}>
<TertiaryButton
containerStyle={{
backgroundColor: $config.PRIMARY_ACTION_BRAND_COLOR,
borderColor: $config.PRIMARY_ACTION_BRAND_COLOR,
}}
onPress={() => {
startBreakoutRoom();
}}
text={'START'}
/>
</View>
</View>
)}
</View>
);
};

const style = StyleSheet.create({
footer: {
width: '100%',
padding: 12,
height: 'auto',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: $config.CARD_LAYER_2_COLOR,
},
bodyContainer: {
flex: 1,
},
});

export default BreakoutRoomView;
Loading