diff --git a/android/app/src/main/java/org/jitsi/meet/MainActivity.java b/android/app/src/main/java/org/jitsi/meet/MainActivity.java index faf38dbc89..883622fd8c 100644 --- a/android/app/src/main/java/org/jitsi/meet/MainActivity.java +++ b/android/app/src/main/java/org/jitsi/meet/MainActivity.java @@ -182,11 +182,6 @@ public class MainActivity extends JitsiMeetActivity { } } - @Override - protected void onConferenceTerminated(HashMap extraData) { - Log.d(TAG, "Conference terminated: " + extraData); - } - // Activity lifecycle method overrides // diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java index 3478a52c97..22eecd0283 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java @@ -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; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java index c8f3bc5034..e76a2d48e8 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java @@ -194,7 +194,6 @@ public class JitsiMeetActivity extends AppCompatActivity protected void onConferenceTerminated(HashMap extraData) { JitsiMeetLogger.i("Conference terminated: " + extraData); - finish(); } protected void onConferenceWillJoin(HashMap 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; } } } diff --git a/ios/app/src/ViewController.m b/ios/app/src/ViewController.m index 0950150c32..a9dadf0f42 100644 --- a/ios/app/src/ViewController.m +++ b/ios/app/src/ViewController.m @@ -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"]); } diff --git a/ios/sdk/src/JitsiMeetViewDelegate.h b/ios/sdk/src/JitsiMeetViewDelegate.h index df21b2bb88..1d9a72ba8b 100644 --- a/ios/sdk/src/JitsiMeetViewDelegate.h +++ b/ios/sdk/src/JitsiMeetViewDelegate.h @@ -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 diff --git a/lang/main.json b/lang/main.json index 16fdc34a11..71739aa267 100644 --- a/lang/main.json +++ b/lang/main.json @@ -39,6 +39,9 @@ "audioOnly": { "audioOnly": "Low bandwidth" }, + "blankPage": { + "meetingEnded": "Meeting ended." + }, "breakoutRooms": { "defaultName": "Breakout room #{{index}}", "mainRoom": "Main room", diff --git a/react/features/base/color-scheme/defaultScheme.js b/react/features/base/color-scheme/defaultScheme.js index 3a1f6ad70b..4cd02cba55 100644 --- a/react/features/base/color-scheme/defaultScheme.js +++ b/react/features/base/color-scheme/defaultScheme.js @@ -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)', diff --git a/react/features/base/media/middleware.js b/react/features/base/media/middleware.js index 95cd35e462..c0d8b9b37e 100644 --- a/react/features/base/media/middleware.js +++ b/react/features/base/media/middleware.js @@ -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); } diff --git a/react/features/mobile/external-api/actionTypes.js b/react/features/mobile/external-api/actionTypes.js index 3a4aed53cd..8f1964170d 100644 --- a/react/features/mobile/external-api/actionTypes.js +++ b/react/features/mobile/external-api/actionTypes.js @@ -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. diff --git a/react/features/mobile/external-api/actions.js b/react/features/mobile/external-api/actions.js index 0464ff8a52..28a76a1aff 100644 --- a/react/features/mobile/external-api/actions.js +++ b/react/features/mobile/external-api/actions.js @@ -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) { return { type: SCREEN_SHARE_PARTICIPANTS_UPDATED, diff --git a/react/features/mobile/external-api/middleware.js b/react/features/mobile/external-api/middleware.js index 1a19724c27..3a4a24eb08 100644 --- a/react/features/mobile/external-api/middleware.js +++ b/react/features/mobile/external-api/middleware.js @@ -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; diff --git a/react/features/overlay/components/native/LoadConfigOverlay.js b/react/features/overlay/components/native/LoadConfigOverlay.js index 234ac7fed2..609053544b 100644 --- a/react/features/overlay/components/native/LoadConfigOverlay.js +++ b/react/features/overlay/components/native/LoadConfigOverlay.js @@ -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 { +class LoadConfigOverlay extends PureComponent { /** * 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 { * @returns {ReactElement} */ render() { - const { _styles } = this.props; - return ( - + - + { this.props.t('connectingOverlay.joiningRoom') } @@ -77,18 +65,5 @@ class LoadConfigOverlay extends Component { } } -/** - * 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); diff --git a/react/features/overlay/components/native/styles.js b/react/features/overlay/components/native/styles.js index c1ec7a9dec..469f1c103d 100644 --- a/react/features/overlay/components/native/styles.js +++ b/react/features/overlay/components/native/styles.js @@ -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') - } -}); diff --git a/react/features/welcome/components/BlankPage.native.js b/react/features/welcome/components/BlankPage.native.js index 939324a7f9..db3b340751 100644 --- a/react/features/welcome/components/BlankPage.native.js +++ b/react/features/welcome/components/BlankPage.native.js @@ -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 -}; - -/** - * 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 { /** * 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 ( + + + { t('blankPage.meetingEnded') } + + + ); +}; - return ( - - - - ); - } -} - -/** - * 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; diff --git a/react/features/welcome/components/WelcomePage.native.js b/react/features/welcome/components/WelcomePage.native.js index b719a50d0c..c003a43dd2 100644 --- a/react/features/welcome/components/WelcomePage.native.js +++ b/react/features/welcome/components/WelcomePage.native.js @@ -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: () => }); - - dispatch(destroyLocalTracks()); } /** diff --git a/react/features/welcome/components/styles.js b/react/features/welcome/components/styles.js index d032a65ac7..2777c6a739 100644 --- a/react/features/welcome/components/styles.js +++ b/react/features/welcome/components/styles.js @@ -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'