feat)rn,sdk) introduce a "ready to close" event

This event is the event host applications need to listen to for knowing when to
dispose the SDK from now on.

Since the introduction of breakout rooms it's possible that we navigate from one
meeting to another, so there will be several conference join / terminations.

In addition, local track destruction is now moved to SET_ROOM when there is no
room, aka, we are going back to the welcome page or to the black page.
This commit is contained in:
Saúl Ibarra Corretgé 2021-11-23 15:06:59 +01:00 committed by Saúl Ibarra Corretgé
parent 763d975445
commit d7b581e338
16 changed files with 108 additions and 139 deletions

View File

@ -182,11 +182,6 @@ public class MainActivity extends JitsiMeetActivity {
}
}
@Override
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
Log.d(TAG, "Conference terminated: " + extraData);
}
// Activity lifecycle method overrides
//

View File

@ -87,7 +87,7 @@ public class BroadcastEvent {
CHAT_MESSAGE_RECEIVED("org.jitsi.meet.CHAT_MESSAGE_RECEIVED"),
CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED"),
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED");
READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE");
private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN";
private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED";
@ -101,6 +101,7 @@ public class BroadcastEvent {
private static final String CHAT_MESSAGE_RECEIVED_NAME = "CHAT_MESSAGE_RECEIVED";
private static final String CHAT_TOGGLED_NAME = "CHAT_TOGGLED";
private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED";
private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE";
private final String action;
@ -147,6 +148,8 @@ public class BroadcastEvent {
return CHAT_TOGGLED;
case VIDEO_MUTED_CHANGED_NAME:
return VIDEO_MUTED_CHANGED;
case READY_TO_CLOSE_NAME:
return READY_TO_CLOSE;
}
return null;

View File

@ -194,7 +194,6 @@ public class JitsiMeetActivity extends AppCompatActivity
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
JitsiMeetLogger.i("Conference terminated: " + extraData);
finish();
}
protected void onConferenceWillJoin(HashMap<String, Object> extraData) {
@ -217,6 +216,11 @@ public class JitsiMeetActivity extends AppCompatActivity
}
}
protected void onReadyToClose() {
JitsiMeetLogger.i("SDK is ready to close");
finish();
}
// Activity lifecycle methods
//
@ -298,6 +302,9 @@ public class JitsiMeetActivity extends AppCompatActivity
case PARTICIPANT_LEFT:
onParticipantLeft(event.getData());
break;
case READY_TO_CLOSE:
onReadyToClose();
break;
}
}
}

View File

@ -91,10 +91,13 @@
#if 0
- (void)enterPictureInPicture:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"ENTER_PICTURE_IN_PICTURE" withData:data];
}
#endif
- (void)readyToClose:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"READY_TO_CLOSE" withData:data];
}
- (void)participantJoined:(NSDictionary *)data {
NSLog(@"%@%@", @"Participant joined: ", data[@"participantId"]);
}

View File

@ -111,4 +111,9 @@
*/
- (void)videoMutedChanged:(NSDictionary *)data;
/**
* Called when the SDK is ready to be closed. No meeting is happening at this point.
*/
- (void)readyToClose:(NSDictionary *)data;
@end

View File

@ -39,6 +39,9 @@
"audioOnly": {
"audioOnly": "Low bandwidth"
},
"blankPage": {
"meetingEnded": "Meeting ended."
},
"breakoutRooms": {
"defaultName": "Breakout room #{{index}}",
"mainRoom": "Main room",

View File

@ -43,9 +43,6 @@ export default {
'LargeVideo': {
background: '#040404'
},
'LoadConfigOverlay': {
background: 'rgb(249, 249, 249)'
},
'Thumbnail': {
activeParticipantHighlight: 'rgb(81, 214, 170)',
activeParticipantTint: 'rgba(49, 183, 106, 0.3)',

View File

@ -14,7 +14,12 @@ import { isRoomValid, SET_ROOM } from '../conference';
import { getLocalParticipant } from '../participants';
import { MiddlewareRegistry } from '../redux';
import { getPropertyValue } from '../settings';
import { isLocalVideoTrackDesktop, setTrackMuted, TRACK_ADDED } from '../tracks';
import {
destroyLocalTracks,
isLocalVideoTrackDesktop,
setTrackMuted,
TRACK_ADDED
} from '../tracks';
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from './actionTypes';
import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions';
@ -217,6 +222,10 @@ function _setRoom({ dispatch, getState }, next, action) {
dispatch(setAudioOnly(audioOnly, false));
if (!roomIsValid) {
dispatch(destroyLocalTracks());
}
return next(action);
}

View File

@ -1,3 +1,12 @@
/**
* The type of the action which indicates the SDK is ready to be closed.
*
* @returns {{
* type: READY_TO_CLOSE
* }}
*/
export const READY_TO_CLOSE = 'READY_TO_CLOSE';
/**
* The type of the action which sets the list of known participant IDs which
* have an active screen share.

View File

@ -1,6 +1,22 @@
// @flow
import { SCREEN_SHARE_PARTICIPANTS_UPDATED } from './actionTypes';
import {
READY_TO_CLOSE,
SCREEN_SHARE_PARTICIPANTS_UPDATED
} from './actionTypes';
/**
* Creates a (redux) action which signals that the SDK is ready to be closed.
*
* @returns {{
* type: READY_TO_CLOSE
* }}
*/
export function readyToClose() {
return {
type: READY_TO_CLOSE
};
}
/**
* Creates a (redux) action which signals that the list of known participants
@ -9,10 +25,10 @@ import { SCREEN_SHARE_PARTICIPANTS_UPDATED } from './actionTypes';
* @param {string} participantIds - The participants which currently have active
* screen share streams.
* @returns {{
* type: SCREEN_SHARE_PARTICIPANTS_UPDATED,
* participantId: string
* }}
*/
* type: SCREEN_SHARE_PARTICIPANTS_UPDATED,
* participantId: string
* }}
*/
export function setParticipantsWithScreenShare(participantIds: Array<string>) {
return {
type: SCREEN_SHARE_PARTICIPANTS_UPDATED,

View File

@ -24,8 +24,6 @@ import {
getURLWithoutParams
} from '../../base/connection';
import {
isFatalJitsiConferenceError,
isFatalJitsiConnectionError,
JitsiConferenceEvents } from '../../base/lib-jitsi-meet';
import { MEDIA_TYPE } from '../../base/media';
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../../base/media/actionTypes';
@ -45,6 +43,7 @@ import { SET_PAGE_RELOAD_OVERLAY_CANCELED } from '../../overlay/actionTypes';
import { muteLocal } from '../../video-menu/actions';
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';
import { READY_TO_CLOSE } from './actionTypes';
import { setParticipantsWithScreenShare } from './actions';
import { sendEvent } from './functions';
import logger from './logger';
@ -116,7 +115,7 @@ MiddlewareRegistry.register(store => next => action => {
// counterpart of the External API (or at least not in the
// fatality/finality semantics attributed to
// conferenceFailed:/onConferenceFailed).
if (!error.recoverable && !isFatalJitsiConnectionError(error) && !isFatalJitsiConferenceError(error)) {
if (!error.recoverable) {
_sendConferenceEvent(store, /* action */ {
error: _toErrorString(error),
...data
@ -190,6 +189,10 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case READY_TO_CLOSE:
sendEvent(store, type, /* data */ {});
break;
case SET_ROOM:
_maybeTriggerEarlyConferenceWillJoin(store, action);
break;

View File

@ -1,16 +1,14 @@
// @flow
import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import { SafeAreaView, Text, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import OverlayFrame from './OverlayFrame';
import styles from './styles';
import styles, { TEXT_COLOR } from './styles';
type Props = {
@ -29,7 +27,7 @@ type Props = {
* Implements an overlay to tell the user that there is an operation in progress in the background during connect
* so then the app doesn't seem hung.
*/
class LoadConfigOverlay extends Component<Props> {
class LoadConfigOverlay extends PureComponent<Props> {
/**
* Determines whether this overlay needs to be rendered (according to a
* specific redux state). Called by {@link OverlayContainer}.
@ -49,25 +47,15 @@ class LoadConfigOverlay extends Component<Props> {
* @returns {ReactElement}
*/
render() {
const { _styles } = this.props;
return (
<OverlayFrame>
<View
style = { [
styles.loadingOverlayWrapper,
_styles.loadingOverlayWrapper
] }>
<View style = { styles.loadingOverlayWrapper }>
<SafeAreaView>
<LoadingIndicator
color = { _styles.indicatorColor }
color = { TEXT_COLOR }
size = 'large'
style = { styles.connectIndicator } />
<Text
style = { [
styles.loadingOverlayText,
_styles.loadingOverlayText
] }>
<Text style = { styles.loadingOverlayText }>
{ this.props.t('connectingOverlay.joiningRoom') }
</Text>
</SafeAreaView>
@ -77,18 +65,5 @@ class LoadConfigOverlay extends Component<Props> {
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {{
* _styles: StyleType
* }}
*/
function _mapStateToProps(state) {
return {
_styles: ColorSchemeRegistry.get(state, 'LoadConfigOverlay')
};
}
export default translate(connect(_mapStateToProps)(LoadConfigOverlay));
export default translate(LoadConfigOverlay);

View File

@ -2,8 +2,10 @@
import { StyleSheet } from 'react-native';
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { BoxModel, ColorPalette } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export const TEXT_COLOR = BaseTheme.palette.text01;
/**
* The React {@code Component} styles of the overlay feature.
@ -23,12 +25,13 @@ export default {
},
loadingOverlayText: {
color: ColorPalette.white
color: TEXT_COLOR
},
loadingOverlayWrapper: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
@ -38,18 +41,3 @@ export default {
flex: 1
}
};
/**
* Color schemed styles for all the component based on the abstract dialog.
*/
ColorSchemeRegistry.register('LoadConfigOverlay', {
indicatorColor: schemeColor('text'),
loadingOverlayText: {
color: schemeColor('text')
},
loadingOverlayWrapper: {
backgroundColor: schemeColor('background')
}
});

View File

@ -1,80 +1,34 @@
// @flow
import React, { Component } from 'react';
import { View } from 'react-native';
import type { Dispatch } from 'redux';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Text, View } from 'react-native';
import { useDispatch } from 'react-redux';
import { ColorSchemeRegistry } from '../../base/color-scheme';
import { LoadingIndicator } from '../../base/react';
import { connect } from '../../base/redux';
import { StyleType } from '../../base/styles';
import { destroyLocalTracks } from '../../base/tracks';
import { readyToClose } from '../../mobile/external-api/actions';
import styles from './styles';
/**
* The type of React {@code Component} props of {@link BlankPage}.
*/
type Props = {
/**
* The color schemed style of the component.
*/
_styles: StyleType,
const BlankPage = () => {
const dispatch = useDispatch();
const { t } = useTranslation();
dispatch: Dispatch<any>
};
/**
* The React {@code Component} displayed by {@code AbstractApp} when it has no
* {@code Route} to render. Renders a progress indicator when there are ongoing
* network requests.
*/
class BlankPage extends Component<Props> {
/**
* Destroys the local tracks (if any) since no media is desired when this
* component is rendered.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
this.props.dispatch(destroyLocalTracks());
}
useEffect(() => {
dispatch(readyToClose());
}, []);
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _styles } = this.props;
return (
<View style = { styles.blankPageWrapper }>
<Text style = { styles.blankPageText }>
{ t('blankPage.meetingEnded') }
</Text>
</View>
);
};
return (
<View
style = { [
styles.blankPageWrapper,
_styles.loadingOverlayWrapper
] }>
<LoadingIndicator
color = { _styles.indicatorColor }
size = 'large' />
</View>
);
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
_styles: ColorSchemeRegistry.get(state, 'LoadConfigOverlay')
};
}
export default connect(_mapStateToProps)(BlankPage);
export default BlankPage;

View File

@ -18,7 +18,6 @@ import { Icon, IconMenu, IconWarning } from '../../base/icons';
import JitsiStatusBar from '../../base/modal/components/JitsiStatusBar';
import { LoadingIndicator, Text } from '../../base/react';
import { connect } from '../../base/redux';
import { destroyLocalTracks } from '../../base/tracks';
import BaseTheme from '../../base/ui/components/BaseTheme.native';
import {
@ -108,7 +107,6 @@ class WelcomePage extends AbstractWelcomePage<*> {
const {
_headerStyles,
dispatch,
navigation
} = this.props;
@ -129,8 +127,6 @@ class WelcomePage extends AbstractWelcomePage<*> {
headerRight: () =>
<VideoSwitch />
});
dispatch(destroyLocalTracks());
}
/**

View File

@ -42,12 +42,18 @@ export default {
marginRight: BaseTheme.spacing[2]
},
blankPageText: {
color: TEXT_COLOR,
fontSize: 18
},
/**
* View that is rendered when there is no welcome page.
*/
blankPageWrapper: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1,
flexDirection: 'column',
justifyContent: 'center'