Compare commits

...

14 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé 14df119373 fix(rn,navigation) wait until the root navigator is initialized
There is a race condition in the root navigatior's initialization.

It's possible that it's initialized a touch too late and SDK users who
try to navigate to a conference end up stuck in the connecting screen
because the navigator is null.

This PR waits for it to be initilized by very unorthodox means, it's a
horrible hack which we need to undo, but for that we need to break
appart the inheritance relationship between App.{web,native},
AbstractApp and BaseApp because it's very inflexible.

The flags are now initialized very early so the naviggator sees if the
welcome page is enabled or not.
2022-05-06 11:27:49 +02:00
Saúl Ibarra Corretgé 1fcb8c8e44 fix(rn,config) fix loading config due to broken import 2022-05-03 17:18:58 +03:00
Gabriel Borlea 36d838cc13 Fixes for highlights mobile (#11209) 2022-04-05 14:39:27 +03:00
Saúl Ibarra Corretgé dbabdf3351 chore(rn,versions) bump app versions 2022-03-29 15:25:27 +02:00
Saúl Ibarra Corretgé 3bcde982ac chore(rn,versions) bump SDK versions 2022-03-29 15:24:08 +02:00
Saúl Ibarra Corretgé beb301e691 fix(android,back-button) rework back button handling on Android
We used to have a registry which registered a single handlerwith RN.
THis was registered really early in the app.

When React Navigation was introduced we ddidn't realize it interacts
with the back button. In a stack nagigator it will navigate to the
previous screen. This meant our back button handling was broken.

This commit removes our previous registry and uses the RN back button
handler directly in the 2 components that use it: the conference and
bottom sheets.

Since these handlers are registered after navigation, our handlers are
going to run first so we cna implement the behavior we need, namely to
dismiss an open botom sheet or set the conference in PiP mode.
2022-03-29 15:21:40 +02:00
Calin Chitu d0836ff651 feat(participants/native) - fix joining breakout room 2022-03-29 15:19:45 +02:00
Saúl Ibarra Corretgé c028511aaf fix(rn,lobby) fix lobby not showing up on subsequent tries
We need to make sure to hide it explicitly so the Redux state is in sync
with reality.
2022-03-29 15:19:44 +02:00
Saúl Ibarra Corretgé 664552bc05 chore(rn,versions) bump SDK versions 2022-03-28 11:22:36 +02:00
Saúl Ibarra Corretgé ac35eea08e feat(ios) enable Dropbox recording 2022-03-25 17:24:06 +01:00
Saúl Ibarra Corretgé 172683d645 fix(rn,recording) fix recording dialog state not updating 2022-03-25 17:23:30 +01:00
Calinteodor 858e83b09e fix(rn,recording) fix start button not being enabled 2022-03-25 11:53:30 +02:00
Calin Chitu e2750ee58e feat(participants/native) - updated container styles 2022-03-25 11:51:01 +02:00
Calin Chitu fe132581d4 fix(mobile/navigation) - fixed bottom color glitch 2022-03-23 19:09:52 +02:00
32 changed files with 215 additions and 199 deletions

View File

@ -26,5 +26,5 @@ android.useAndroidX=true
android.enableJetifier=true
android.bundle.enableUncompressedNativeLibs=false
appVersion=22.1.0
sdkVersion=5.0.0
appVersion=22.1.1
sdkVersion=5.0.2

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>22.1.0</string>
<string>22.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View File

@ -37,13 +37,8 @@
[builder setFeatureFlag:@"welcomepage.enabled" withBoolean:YES];
[builder setFeatureFlag:@"resolution" withValue:@(360)];
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
// Apple rejected our app because they claim requiring a
// Dropbox account for recording is not acceptable.
#if DEBUG
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
#endif
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
}];
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>22.1.0</string>
<string>22.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>22.1.0</string>
<string>22.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>22.1.0</string>
<string>22.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>5.0.0</string>
<string>5.0.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

@ -12,7 +12,7 @@ import {
storeConfig
} from '../base/config';
import { connect, disconnect, setLocationURL } from '../base/connection';
import { loadConfig } from '../base/lib-jitsi-meet';
import { loadConfig } from '../base/lib-jitsi-meet/functions.native';
import { createDesiredLocalTracks } from '../base/tracks';
import {
getBackendSafeRoomName,

View File

@ -27,11 +27,6 @@ declare var __DEV__;
*/
type Props = AbstractAppProps & {
/**
* An object of colors that override the default colors of the app/sdk.
*/
colorScheme: ?Object,
/**
* Identifier for this app on the native side.
*/
@ -98,13 +93,31 @@ export class App extends AbstractApp {
*/
async _extraInit() {
const { dispatch, getState } = this.state.store;
// We set these early enough so then we avoid any unnecessary re-renders.
dispatch(updateFlags(this.props.flags));
const route = await _getRouteToRender();
// We need the root navigator to be set early.
await this._navigate(route);
// We set these early enough so then we avoid any unnecessary re-renders.
dispatch(updateFlags(this.props.flags));
// HACK ALERT!
// Wait until the root navigator is ready.
// We really need to break the inheritance relationship between App,
// AbstractApp and BaseApp, it's very inflexible and cumbersome right now.
const rootNavigationReady = new Promise(resolve => {
const i = setInterval(() => {
const { ready } = getState()['features/app'] || {};
if (ready) {
clearInterval(i);
resolve();
}
}, 50);
});
await rootNavigationReady;
// Check if serverURL is configured externally and not allowed to change.
const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);

View File

@ -2,7 +2,6 @@
import '../authentication/middleware';
import '../mobile/audio-mode/middleware';
import '../mobile/back-button/middleware';
import '../mobile/background/middleware';
import '../mobile/call-integration/middleware';
import '../mobile/external-api/middleware';

View File

@ -0,0 +1,22 @@
import { ReducerRegistry } from '../base/redux';
import { _ROOT_NAVIGATION_READY } from '../mobile/navigation/actionTypes';
/**
* Listen for actions which changes the state of the app feature.
*
* @param {Object} state - The Redux state of the feature features/app.
* @param {Object} action - Action object.
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/app', (state = {}, action) => {
switch (action.type) {
case _ROOT_NAVIGATION_READY:
return {
...state,
ready: action.ready
};
default:
return state;
}
});

View File

@ -8,4 +8,6 @@ import '../mobile/full-screen/reducer';
import '../mobile/watchos/reducer';
import '../shared-video/reducer';
import './reducer.native';
import './reducers.any';

View File

@ -3,12 +3,12 @@
import React, { PureComponent, type Node } from 'react';
import {
Animated,
BackHandler,
Dimensions,
TouchableWithoutFeedback,
View
} from 'react-native';
import { BackButtonRegistry } from '../../../../mobile/back-button';
import { type StyleType } from '../../../styles';
import styles from './slidingviewstyles';
@ -121,7 +121,7 @@ export default class SlidingView extends PureComponent<Props, State> {
* @inheritdoc
*/
componentDidMount() {
BackButtonRegistry.addListener(this._onHardwareBackPress, true);
BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress);
this._mounted = true;
this._setShow(this.props.show);
@ -146,7 +146,7 @@ export default class SlidingView extends PureComponent<Props, State> {
* @inheritdoc
*/
componentWillUnmount() {
BackButtonRegistry.removeListener(this._onHardwareBackPress);
BackHandler.removeEventListener('hardwareBackPress', this._onHardwareBackPress);
this._mounted = false;
}
@ -229,13 +229,9 @@ export default class SlidingView extends PureComponent<Props, State> {
* @returns {boolean}
*/
_onHardwareBackPress() {
const { onHide } = this.props;
this._onHide();
if (typeof onHide === 'function') {
return onHide();
}
return false;
return true;
}
_onHide: () => void;

View File

@ -1,7 +1,7 @@
// @flow
import React from 'react';
import { NativeModules, SafeAreaView, StatusBar, View } from 'react-native';
import { BackHandler, NativeModules, SafeAreaView, StatusBar, View } from 'react-native';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { appNavigate } from '../../../app/actions';
@ -22,7 +22,6 @@ import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { KnockingParticipantList } from '../../../lobby/components/native';
import { getIsLobbyVisible } from '../../../lobby/functions';
import { BackButtonRegistry } from '../../../mobile/back-button';
import { navigate }
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
@ -166,7 +165,7 @@ class Conference extends AbstractConference<Props, State> {
* @returns {void}
*/
componentDidMount() {
BackButtonRegistry.addListener(this._onHardwareBackPress);
BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress);
}
/**
@ -196,7 +195,7 @@ class Conference extends AbstractConference<Props, State> {
*/
componentWillUnmount() {
// Tear handling any hardware button presses for back navigation down.
BackButtonRegistry.removeListener(this._onHardwareBackPress);
BackHandler.removeEventListener('hardwareBackPress', this._onHardwareBackPress);
clearTimeout(this._expandedLabelTimeout.current);
}

View File

@ -1,9 +1,9 @@
// @flow
import { type Dispatch } from 'redux';
import { batch } from 'react-redux';
import { appNavigate } from '../app/actions';
import { hideLobbyScreen, setKnockingState } from './actions.any';
export * from './actions.any';
/**
@ -12,8 +12,11 @@ export * from './actions.any';
* @returns {Function}
*/
export function cancelKnocking() {
return async (dispatch: Dispatch<any>) => {
dispatch(appNavigate(undefined));
return dispatch => {
batch(() => {
dispatch(setKnockingState(false));
dispatch(hideLobbyScreen());
dispatch(appNavigate(undefined));
});
};
}

View File

@ -58,7 +58,7 @@ export function showLobbyChatButton(
const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
const conference = getCurrentConference(state);
const lobbyLocalId = conference.myLobbyUserId();
const lobbyLocalId = conference?.myLobbyUserId();
if (!enableLobbyChat) {
return false;

View File

@ -1,66 +0,0 @@
// @flow
/**
* An registry that dispatches hardware back button events for subscribers with a custom logic.
*/
class BackButtonRegistry {
_listeners: Array<Function>;
/**
* Instantiates a new instance of the registry.
*/
constructor() {
this._listeners = [];
}
/**
* Adds a listener to the registry.
*
* NOTE: Due to the different order of component mounts, we allow a component to register
* its listener to the top of the list, so then that will be invoked before other, 'non-top'
* listeners. For example a 'non-top' listener can be the one that puts the app into PiP mode,
* while a 'top' listener is the one that closes a modal in a conference.
*
* @param {Function} listener - The listener function.
* @param {boolean?} top - If true, the listener will be put on the top (eg for modal-like components).
* @returns {void}
*/
addListener(listener: Function, top: boolean = false) {
if (top) {
this._listeners.splice(0, 0, listener);
} else {
this._listeners.push(listener);
}
}
/**
* Removes a listener from the registry.
*
* @param {Function} listener - The listener to remove.
* @returns {void}
*/
removeListener(listener: Function) {
this._listeners = this._listeners.filter(f => f !== listener);
}
onHardwareBackPress: () => boolean;
/**
* Callback for the back button press event.
*
* @returns {boolean}
*/
onHardwareBackPress() {
for (const listener of this._listeners) {
const result = listener();
if (result === true) {
return true;
}
}
return false;
}
}
export default new BackButtonRegistry();

View File

@ -1,3 +0,0 @@
// @flow
export { default as BackButtonRegistry } from './BackButtonRegistry';

View File

@ -1,36 +0,0 @@
// @flow
import { BackHandler } from 'react-native';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
import { MiddlewareRegistry } from '../../base/redux';
import BackButtonRegistry from './BackButtonRegistry';
// Binding function to the proper instance, so then the event emitter won't replace the
// underlying instance.
BackButtonRegistry.onHardwareBackPress = BackButtonRegistry.onHardwareBackPress.bind(BackButtonRegistry);
/**
* Middleware that captures App lifetime actions and subscribes to application
* state changes. When the application state changes it will fire the action
* required to mute or unmute the local video in case the application goes to
* the background or comes back from it.
*
* @param {Store} store - The redux store.
* @returns {Function}
* @see {@link https://facebook.github.io/react-native/docs/appstate.html}
*/
MiddlewareRegistry.register(() => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
BackHandler.addEventListener('hardwareBackPress', BackButtonRegistry.onHardwareBackPress);
break;
case APP_WILL_UNMOUNT:
BackHandler.removeEventListener('hardwareBackPress', BackButtonRegistry.onHardwareBackPress);
break;
}
return next(action);
});

View File

@ -0,0 +1 @@
export const _ROOT_NAVIGATION_READY = '_ROOT_NAVIGATION_READY';

View File

@ -2,11 +2,12 @@
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import React, { useCallback } from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { connect } from '../../../base/redux';
import { DialInSummary } from '../../../invite';
import { _ROOT_NAVIGATION_READY } from '../actionTypes';
import { rootNavigationRef } from '../rootNavigationContainerRef';
import { screen } from '../routes';
import {
@ -26,6 +27,11 @@ const RootStack = createStackNavigator();
type Props = {
/**
* Redux dispatch function.
*/
dispatch: Function,
/**
* Is welcome page available?
*/
@ -33,14 +39,21 @@ type Props = {
}
const RootNavigationContainer = ({ isWelcomePageAvailable }: Props) => {
const RootNavigationContainer = ({ dispatch, isWelcomePageAvailable }: Props) => {
const initialRouteName = isWelcomePageAvailable
? screen.root : screen.connecting;
const onReady = useCallback(() => {
dispatch({
type: _ROOT_NAVIGATION_READY,
ready: true
});
}, [ dispatch ]);
return (
<SafeAreaProvider>
<NavigationContainer
independent = { true }
onReady = { onReady }
ref = { rootNavigationRef }
theme = { navigationContainerTheme }>
<RootStack.Navigator
@ -89,4 +102,3 @@ function mapStateToProps(state: Object) {
}
export default connect(mapStateToProps)(RootNavigationContainer);

View File

@ -1,5 +1,3 @@
import { StyleSheet } from 'react-native';
import { BoxModel } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme';
@ -11,13 +9,12 @@ export const TEXT_COLOR = BaseTheme.palette.text01;
*/
export const navigationStyles = {
connectingScreenContainer: {
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1
},
connectingScreenContent: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1,
flexDirection: 'column',
justifyContent: 'center'

View File

@ -71,8 +71,8 @@ export default {
},
transparentButton: {
...baseButton,
backgroundColor: 'transparent'
backgroundColor: 'transparent',
marginTop: BaseTheme.spacing[3]
},
leaveButtonLabel: {

View File

@ -31,7 +31,7 @@ type Props = {
}
const CollapsibleList = ({ children, containerStyle, onLongPress, title }: Props) => {
const [ collapsed, setCollapsed ] = useState(true);
const [ collapsed, setCollapsed ] = useState(false);
const _toggleCollapsed = useCallback(() => {
setCollapsed(!collapsed);
}, [ collapsed ]);

View File

@ -50,11 +50,21 @@ type Props = {
*/
_sortedRemoteParticipants: Map<string, string>,
/**
* List of breakout rooms that were created.
*/
breakoutRooms: Array,
/**
* The redux dispatch function.
*/
dispatch: Function,
/**
* List of participants waiting in lobby.
*/
lobbyParticipants: Array,
/**
* Participants search string.
*/
@ -180,6 +190,8 @@ class MeetingParticipantList extends PureComponent<Props> {
_participantsCount,
_showInviteButton,
_sortedRemoteParticipants,
breakoutRooms,
lobbyParticipants,
t
} = this.props;
const title = _currentRoom?.name
@ -192,12 +204,19 @@ class MeetingParticipantList extends PureComponent<Props> {
// Regarding the fact that we have 3 sections, we apply
// a certain height percentage for every section in order for all to fit
// inside the participants pane container
// If there are only meeting participants available,
// we take the full container height
const onlyMeetingParticipants
= breakoutRooms?.length === 0 && lobbyParticipants.length === 0;
const containerStyle
= _participantsCount > 3 && styles.meetingListContainer;
= onlyMeetingParticipants
? styles.meetingListFullContainer : styles.meetingListContainer;
const finalContainerStyle
= _participantsCount > 3 && containerStyle;
return (
<CollapsibleList
containerStyle = { containerStyle }
containerStyle = { finalContainerStyle }
title = { title } >
{
_showInviteButton

View File

@ -19,6 +19,10 @@ import {
getCurrentRoomId,
isInBreakoutRoom
} from '../../../breakout-rooms/functions';
import {
getKnockingParticipants,
getLobbyEnabled
} from '../../../lobby/functions';
import MuteEveryoneDialog
from '../../../video-menu/components/native/MuteEveryoneDialog';
import {
@ -63,11 +67,15 @@ const ParticipantsPane = () => {
&& participantsCount > 2 && rooms.length > 1;
const addBreakoutRoom
= _isBreakoutRoomsSupported && !hideAddRoomButton && isLocalModerator;
const lobbyEnabled = useSelector(getLobbyEnabled);
const lobbyParticipants = useSelector(getKnockingParticipants);
return (
<JitsiScreen style = { styles.participantsPaneContainer }>
<LobbyParticipantList />
<MeetingParticipantList
breakoutRooms = { _isBreakoutRoomsSupported && rooms }
lobbyParticipants = { lobbyEnabled && lobbyParticipants }
searchString = { searchString }
setSearchString = { setSearchString } />
{

View File

@ -196,7 +196,7 @@ export default {
},
lobbyListContent: {
height: '20%'
height: '16%'
},
lobbyListDescription: {
@ -217,7 +217,11 @@ export default {
},
meetingListContainer: {
height: '60%'
height: '56%'
},
meetingListFullContainer: {
height: '82%'
},
meetingListDescription: {

View File

@ -379,10 +379,7 @@ class StartRecordingDialogContent extends Component<Props> {
<Container>
<Container
className = 'recording-header recording-header-line'
style = { [
styles.headerIntegrations,
_dialogStyles.topBorderContainer
] }>
style = { styles.headerIntegrations }>
<Container
className = 'recording-icon-container'>
<Image

View File

@ -9,6 +9,7 @@ import HeaderNavigationButton
from '../../../../mobile/navigation/components/HeaderNavigationButton';
import { goBack } from
'../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { RECORDING_TYPES } from '../../../constants';
import AbstractStartRecordingDialog, {
type Props,
mapStateToProps
@ -43,30 +44,38 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
* @returns {void}
*/
componentDidMount() {
const {
_fileRecordingsServiceEnabled,
_isDropboxEnabled,
navigation,
t
} = this.props;
super.componentDidMount();
const {
isTokenValid,
isValidating
} = this.state;
// disable start button id recording service is shown only, when
// validating dropbox token, if that is not enabled we either always
// show the start button or if just dropbox is enabled start is available
// when there is token
const isStartDisabled
= _fileRecordingsServiceEnabled ? isValidating
: _isDropboxEnabled ? !isTokenValid : false;
const { navigation, t } = this.props;
navigation.setOptions({
headerRight: () => (
<HeaderNavigationButton
disabled = { isStartDisabled }
disabled = { this.isStartRecordingDisabled() }
label = { t('dialog.start') }
onPress = { this._onStartPress }
twoActions = { true } />
)
});
}
/**
* Implements React's {@link Component#componentDidUpdate()}. Invoked
* immediately after this component is updated.
*
* @inheritdoc
* @returns {void}
*/
componentDidUpdate(prevProps) {
super.componentDidUpdate(prevProps);
const { navigation, t } = this.props;
navigation.setOptions({
// eslint-disable-next-line react/no-multi-comp
headerRight: () => (
<HeaderNavigationButton
disabled = { this.isStartRecordingDisabled() }
label = { t('dialog.start') }
onPress = { this._onStartPress }
twoActions = { true } />
@ -85,6 +94,30 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
this._onSubmit() && goBack();
}
isStartRecordingDisabled: () => boolean;
/**
* Disables start recording button.
*
* @returns {boolean}
*/
isStartRecordingDisabled() {
const { isTokenValid, selectedRecordingService } = this.state;
// Start button is disabled if recording service is only shown;
// When validating dropbox token, if that is not enabled, we either always
// show the start button or, if just dropbox is enabled, start button
// is available when there is token.
if (selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) {
return false;
} else if (selectedRecordingService === RECORDING_TYPES.DROPBOX) {
return !isTokenValid;
}
return true;
}
/**
* Implements React's {@link Component#render()}.
*

View File

@ -42,7 +42,8 @@ const title = {
const baseHighlightDialogButton = {
borderRadius: BaseTheme.shape.borderRadius,
height: BaseTheme.spacing[7],
flex: 1
flex: 1,
justifyContent: 'space-around'
};
const baseHighlightDialogLabel = {
@ -101,7 +102,7 @@ export default {
},
highlightDialogButtonsContainer: {
display: 'flex',
flexDirection: 'row'
flexDirection: 'column-reverse'
},
highlightDialogCancelButton: {
...baseHighlightDialogButton,
@ -120,8 +121,8 @@ export default {
color: BaseTheme.palette.text01
},
highlightDialogButtonsSpace: {
width: 16,
height: '100%'
height: 16,
width: '100%'
}
};

View File

@ -7,6 +7,7 @@ import { translate } from '../../../../base/i18n';
import { connect } from '../../../../base/redux';
import { toggleScreenshotCaptureSummary } from '../../../../screenshot-capture';
import { isScreenshotCaptureEnabled } from '../../../../screenshot-capture/functions';
import { RECORDING_TYPES } from '../../../constants';
import AbstractStartRecordingDialog, {
mapStateToProps as abstractMapStateToProps
} from '../AbstractStartRecordingDialog';
@ -19,6 +20,30 @@ import StartRecordingDialogContent from '../StartRecordingDialogContent';
* @augments Component
*/
class StartRecordingDialog extends AbstractStartRecordingDialog {
isStartRecordingDisabled: () => boolean;
/**
* Disables start recording button.
*
* @returns {boolean}
*/
isStartRecordingDisabled() {
const { isTokenValid, selectedRecordingService } = this.state;
// Start button is disabled if recording service is only shown;
// When validating dropbox token, if that is not enabled, we either always
// show the start button or, if just dropbox is enabled, start button
// is available when there is token.
if (selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) {
return false;
} else if (selectedRecordingService === RECORDING_TYPES.DROPBOX) {
return !isTokenValid;
}
return true;
}
/**
* Implements React's {@link Component#render()}.
*
@ -33,19 +58,14 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
spaceLeft,
userName
} = this.state;
const { _fileRecordingsServiceEnabled, _fileRecordingsServiceSharingEnabled, _isDropboxEnabled } = this.props;
// disable ok button id recording service is shown only, when
// validating dropbox token, if that is not enabled we either always
// show the ok button or if just dropbox is enabled ok is available
// when there is token
const isOkDisabled
= _fileRecordingsServiceEnabled ? isValidating
: _isDropboxEnabled ? !isTokenValid : false;
const {
_fileRecordingsServiceEnabled,
_fileRecordingsServiceSharingEnabled
} = this.props;
return (
<Dialog
okDisabled = { isOkDisabled }
okDisabled = { this.isStartRecordingDisabled() }
okKey = 'dialog.startRecording'
onSubmit = { this._onSubmit }
titleKey = 'dialog.startRecording'

View File

@ -155,7 +155,7 @@ export async function sendMeetingHighlight(state: Object) {
const reqBody = {
meetingFqn: extractFqnFromPath(state),
sessionId: conference.sessionId,
sessionId: conference.getMeetingUniqueId(),
submitted: Date.now(),
participantId: localParticipant.jwtId,
participantName: localParticipant.name,