mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
feat(invite/native): add people functionality fixes (#13240)
feat(invite/native): add people functionality fixes
This commit is contained in:
@@ -13,7 +13,7 @@ type Props = {
|
||||
/**
|
||||
* Adds bottom padding.
|
||||
*/
|
||||
addBottomPadding: boolean,
|
||||
addBottomPadding?: boolean,
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView content container.
|
||||
|
||||
@@ -3,14 +3,18 @@ import { getGravatarURL } from '@jitsi/js-utils/avatar';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { isStageFilmstripAvailable } from '../../filmstrip/functions';
|
||||
import { isAddPeopleEnabled, isDialOutEnabled } from '../../invite/functions';
|
||||
import { toggleShareDialog } from '../../share-room/actions';
|
||||
import { IStateful } from '../app/types';
|
||||
import { GRAVATAR_BASE_URL } from '../avatar/constants';
|
||||
import { isCORSAvatarURL } from '../avatar/functions';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { ADD_PEOPLE_ENABLED } from '../flags/constants';
|
||||
import { getFeatureFlag } from '../flags/functions';
|
||||
import i18next from '../i18n/i18next';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions.any';
|
||||
import { createDeferred } from '../util/helpers';
|
||||
|
||||
import {
|
||||
@@ -22,6 +26,7 @@ import {
|
||||
import { preloadImage } from './preloadImage';
|
||||
import { FakeParticipant, IJitsiParticipant, IParticipant, ISourceInfo } from './types';
|
||||
|
||||
|
||||
/**
|
||||
* Temp structures for avatar urls to be checked/preloaded.
|
||||
*/
|
||||
@@ -706,3 +711,32 @@ export function getRaiseHandsQueue(stateful: IStateful): Array<{ id: string; rai
|
||||
export function hasRaisedHand(participant?: IParticipant): boolean {
|
||||
return Boolean(participant?.raisedHandTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add people feature enabling/disabling.
|
||||
*
|
||||
* @param {Object|Function} stateful - Object or function that can be resolved
|
||||
* to the Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const addPeopleFeatureControl = (stateful: IStateful) => {
|
||||
const state = toState(stateful);
|
||||
|
||||
return getFeatureFlag(state, ADD_PEOPLE_ENABLED, true)
|
||||
&& (isAddPeopleEnabled(state) || isDialOutEnabled(state));
|
||||
};
|
||||
|
||||
/**
|
||||
* Controls share dialog visibility.
|
||||
*
|
||||
* @param {boolean} addPeopleFeatureEnabled - Checks if add people functionality is enabled.
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export const setShareDialogVisiblity = (addPeopleFeatureEnabled: boolean, dispatch: Function) => {
|
||||
if (addPeopleFeatureEnabled) {
|
||||
dispatch(toggleShareDialog(false));
|
||||
} else {
|
||||
dispatch(toggleShareDialog(true));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,6 +52,8 @@ export type Item = {
|
||||
*/
|
||||
title: string;
|
||||
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Item url.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -9,12 +9,15 @@ import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconAddUser } from '../../../base/icons/svg';
|
||||
import { getParticipantCountWithFake } from '../../../base/participants/functions';
|
||||
import {
|
||||
addPeopleFeatureControl,
|
||||
getParticipantCountWithFake,
|
||||
setShareDialogVisiblity
|
||||
} from '../../../base/participants/functions';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import { toggleShareDialog } from '../../../share-room/actions';
|
||||
import { getInviteOthersControl } from '../../../share-room/functions';
|
||||
|
||||
// @ts-ignore
|
||||
@@ -31,6 +34,11 @@ interface IProps extends WithTranslation {
|
||||
*/
|
||||
_inviteOthersControl: any;
|
||||
|
||||
/**
|
||||
* Checks if add-people feature is enabled.
|
||||
*/
|
||||
_isAddPeopleFeatureEnabled: boolean;
|
||||
|
||||
/**
|
||||
* True if currently in a breakout room.
|
||||
*/
|
||||
@@ -87,7 +95,7 @@ class LonelyMeetingExperience extends PureComponent<IProps> {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.lonelyMeetingContainer }>
|
||||
<View style = { styles.lonelyMeetingContainer as ViewStyle }>
|
||||
<Text style = { styles.lonelyMessage }>
|
||||
{ t('lonelyMeetingExperience.youAreAlone') }
|
||||
</Text>
|
||||
@@ -116,8 +124,11 @@ class LonelyMeetingExperience extends PureComponent<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPress() {
|
||||
this.props.dispatch(toggleShareDialog(true));
|
||||
this.props.dispatch(doInvitePeople());
|
||||
const { _isAddPeopleFeatureEnabled, dispatch } = this.props;
|
||||
|
||||
setShareDialogVisiblity(_isAddPeopleFeatureEnabled, dispatch);
|
||||
|
||||
dispatch(doInvitePeople());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +144,11 @@ function _mapStateToProps(state: IReduxState) {
|
||||
const { conference } = state['features/base/conference'];
|
||||
const _inviteOthersControl = getInviteOthersControl(state);
|
||||
const flag = getFeatureFlag(state, INVITE_ENABLED, true);
|
||||
const _isAddPeopleFeatureEnabled = addPeopleFeatureControl(state);
|
||||
const _isInBreakoutRoom = isInBreakoutRoom(state);
|
||||
|
||||
return {
|
||||
_isAddPeopleFeatureEnabled,
|
||||
_inviteOthersControl,
|
||||
_isInBreakoutRoom,
|
||||
_isInviteFunctionsDisabled: Boolean(!flag || disableInviteFunctions),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import { IStore } from '../app/types';
|
||||
import { ADD_PEOPLE_ENABLED } from '../base/flags/constants';
|
||||
import { getFeatureFlag } from '../base/flags/functions';
|
||||
import { addPeopleFeatureControl } from '../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import { navigate } from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
@@ -9,7 +8,6 @@ import { screen } from '../mobile/navigation/routes';
|
||||
import { beginShareRoom } from '../share-room/actions';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
import { isAddPeopleEnabled, isDialOutEnabled } from './functions';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
@@ -22,10 +20,8 @@ export * from './actions.any';
|
||||
export function doInvitePeople() {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const addPeopleEnabled = getFeatureFlag(state, ADD_PEOPLE_ENABLED, true)
|
||||
&& (isAddPeopleEnabled(state) || isDialOutEnabled(state));
|
||||
|
||||
if (addPeopleEnabled) {
|
||||
if (addPeopleFeatureControl(state)) {
|
||||
return navigate(screen.conference.invite);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { IReduxState } from '../../../app/types';
|
||||
import { showErrorNotification, showNotification } from '../../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../notifications/constants';
|
||||
import { INotificationProps } from '../../../notifications/types';
|
||||
import { invite } from '../../actions';
|
||||
import { invite } from '../../actions.any';
|
||||
import { INVITE_TYPES } from '../../constants';
|
||||
import {
|
||||
getInviteResultsForQuery,
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import _ from 'lodash';
|
||||
import React, { ReactElement } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
TouchableOpacity,
|
||||
View
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { openDialog } from '../../../../base/dialog/actions';
|
||||
import AlertDialog from '../../../../base/dialog/components/native/AlertDialog';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
@@ -19,6 +23,7 @@ import {
|
||||
IconSearch,
|
||||
IconShare
|
||||
} from '../../../../base/icons/svg';
|
||||
// @ts-ignore
|
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
||||
import AvatarListItem from '../../../../base/react/components/native/AvatarListItem';
|
||||
import { Item } from '../../../../base/react/types';
|
||||
@@ -29,64 +34,70 @@ import HeaderNavigationButton
|
||||
import { beginShareRoom } from '../../../../share-room/actions';
|
||||
import { INVITE_TYPES } from '../../../constants';
|
||||
import AbstractAddPeopleDialog, {
|
||||
// @ts-ignore
|
||||
type Props as AbstractProps,
|
||||
// @ts-ignore
|
||||
type State as AbstractState,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractAddPeopleDialog';
|
||||
|
||||
// @ts-ignore
|
||||
import styles, { AVATAR_SIZE } from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* True if the invite dialog should be open, false otherwise.
|
||||
*/
|
||||
_isVisible: boolean,
|
||||
_isVisible: boolean;
|
||||
|
||||
/**
|
||||
* Default prop for navigation between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
navigation: Object;
|
||||
|
||||
/**
|
||||
* Function used to translate i18n labels.
|
||||
*/
|
||||
t: Function,
|
||||
t: Function;
|
||||
|
||||
/**
|
||||
* Theme used for styles.
|
||||
*/
|
||||
theme: Object
|
||||
};
|
||||
theme: Object;
|
||||
}
|
||||
|
||||
type State = AbstractState & {
|
||||
interface IState extends AbstractState {
|
||||
|
||||
/**
|
||||
* Boolean to show if an extra padding needs to be added to the bottom bar.
|
||||
*/
|
||||
bottomPadding: boolean,
|
||||
bottomPadding: boolean;
|
||||
|
||||
/**
|
||||
* State variable to keep track of the search field value.
|
||||
*/
|
||||
fieldValue: string,
|
||||
fieldValue: string;
|
||||
|
||||
/**
|
||||
* True if a search is in progress, false otherwise.
|
||||
*/
|
||||
searchInprogress: boolean,
|
||||
searchInprogress: boolean;
|
||||
|
||||
/**
|
||||
* An array of items that are selectable on this dialog. This is usually
|
||||
* populated by an async search.
|
||||
*/
|
||||
selectableItems: Array<Object>
|
||||
};
|
||||
selectableItems: Array<Object>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a special dialog to invite people from a directory service.
|
||||
*/
|
||||
class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
class AddPeopleDialog
|
||||
// @ts-ignore
|
||||
extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
/**
|
||||
* Default state object to reset the state to when needed.
|
||||
*/
|
||||
@@ -105,14 +116,14 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
*/
|
||||
|
||||
/* eslint-disable-next-line no-undef */
|
||||
searchTimeout: TimeoutID;
|
||||
searchTimeout: number;
|
||||
|
||||
/**
|
||||
* Contrustor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = this.defaultState;
|
||||
@@ -140,11 +151,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
componentDidMount() {
|
||||
const { navigation, t } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
navigation.setOptions({
|
||||
headerRight: () => (
|
||||
<HeaderNavigationButton
|
||||
disabled = { this._isAddDisabled() }
|
||||
label = { t('inviteDialog.send') }
|
||||
style = { styles.sendBtn }
|
||||
twoActions = { true } />
|
||||
)
|
||||
});
|
||||
@@ -155,9 +168,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
const { navigation, t } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
navigation.setOptions({
|
||||
// eslint-disable-next-line react/no-multi-comp
|
||||
headerRight: () => (
|
||||
@@ -165,6 +179,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
disabled = { this._isAddDisabled() }
|
||||
label = { t('inviteDialog.send') }
|
||||
onPress = { this._onInvite }
|
||||
style = { styles.sendBtn }
|
||||
twoActions = { true } />
|
||||
)
|
||||
});
|
||||
@@ -182,9 +197,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
// @ts-ignore
|
||||
_addPeopleEnabled,
|
||||
// @ts-ignore
|
||||
_dialOutEnabled
|
||||
} = this.props;
|
||||
// @ts-ignore
|
||||
const { inviteItems, selectableItems } = this.state;
|
||||
|
||||
let placeholderKey = 'searchPlaceholder';
|
||||
@@ -212,7 +230,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
data = { inviteItems }
|
||||
horizontal = { true }
|
||||
keyExtractor = { this._keyExtractor }
|
||||
renderItem = { this._renderInvitedItem } />
|
||||
renderItem = { this._renderInvitedItem as any } />
|
||||
</View> }
|
||||
<View style = { styles.resultList }>
|
||||
<FlatList
|
||||
@@ -220,7 +238,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
data = { selectableItems }
|
||||
extraData = { inviteItems }
|
||||
keyExtractor = { this._keyExtractor }
|
||||
renderItem = { this._renderItem } />
|
||||
renderItem = { this._renderItem as any } />
|
||||
</View>
|
||||
</JitsiScreen>
|
||||
);
|
||||
@@ -241,7 +259,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* @param {Object} flatListItem - An item of the data array of the {@code FlatList}.
|
||||
* @returns {?Object}
|
||||
*/
|
||||
_getRenderableItem(flatListItem) {
|
||||
_getRenderableItem(flatListItem: any) {
|
||||
const { item } = flatListItem;
|
||||
|
||||
switch (item.type) {
|
||||
@@ -262,12 +280,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_invite: Array<Object> => Promise<Array<Object>>;
|
||||
|
||||
_isAddDisabled: () => boolean;
|
||||
|
||||
_keyExtractor: Object => string;
|
||||
|
||||
/**
|
||||
* Key extractor for the flatlist.
|
||||
*
|
||||
@@ -275,12 +287,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* generated for.
|
||||
* @returns {string}
|
||||
*/
|
||||
_keyExtractor(item) {
|
||||
_keyExtractor(item: any) {
|
||||
return item.type === INVITE_TYPES.USER ? item.id || item.user_id : item.number;
|
||||
}
|
||||
|
||||
_onClearField: () => void;
|
||||
|
||||
/**
|
||||
* Callback to clear the text field.
|
||||
*
|
||||
@@ -292,21 +302,22 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
});
|
||||
|
||||
// Clear search results
|
||||
// @ts-ignore
|
||||
this._onTypeQuery('');
|
||||
}
|
||||
|
||||
_onInvite: () => void;
|
||||
|
||||
/**
|
||||
* Invites the selected entries.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onInvite() {
|
||||
// @ts-ignore
|
||||
this._invite(this.state.inviteItems)
|
||||
.then(invitesLeftToSend => {
|
||||
.then((invitesLeftToSend: ArrayLike<any>) => {
|
||||
if (invitesLeftToSend.length) {
|
||||
this.setState({
|
||||
// @ts-ignore
|
||||
inviteItems: invitesLeftToSend
|
||||
});
|
||||
this._showFailedInviteAlert();
|
||||
@@ -314,55 +325,56 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
_onPressItem: Item => Function;
|
||||
|
||||
/**
|
||||
* Function to prepare a callback for the onPress event of the touchable.
|
||||
*
|
||||
* @param {Item} item - The item on which onPress was invoked.
|
||||
* @returns {Function}
|
||||
*/
|
||||
_onPressItem(item) {
|
||||
_onPressItem(item: Item) {
|
||||
return () => {
|
||||
// @ts-ignore
|
||||
const { inviteItems } = this.state;
|
||||
const finderKey = item.type === INVITE_TYPES.PHONE ? 'number' : 'user_id';
|
||||
|
||||
if (inviteItems.find(
|
||||
_.matchesProperty(finderKey, item[finderKey]))) {
|
||||
// @ts-ignore
|
||||
_.matchesProperty(finderKey, item[finderKey]))) {
|
||||
// Item is already selected, need to unselect it.
|
||||
this.setState({
|
||||
// @ts-ignore
|
||||
inviteItems: inviteItems.filter(
|
||||
element => item[finderKey] !== element[finderKey])
|
||||
// @ts-ignore
|
||||
(element: any) => item[finderKey] !== element[finderKey])
|
||||
});
|
||||
} else {
|
||||
// Item is not selected yet, need to add to the list.
|
||||
const items: Array<Object> = inviteItems.concat(item);
|
||||
|
||||
this.setState({
|
||||
// @ts-ignore
|
||||
inviteItems: _.sortBy(items, [ 'name', 'number' ])
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_onShareMeeting: () => void;
|
||||
|
||||
/**
|
||||
* Shows the system share sheet to share the meeting information.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShareMeeting() {
|
||||
// @ts-ignore
|
||||
if (this.state.inviteItems.length > 0) {
|
||||
// The use probably intended to invite people.
|
||||
this._onInvite();
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.props.dispatch(beginShareRoom());
|
||||
}
|
||||
}
|
||||
|
||||
_onTypeQuery: string => void;
|
||||
|
||||
/**
|
||||
* Handles the typing event of the text field on the dialog and performs the
|
||||
* search.
|
||||
@@ -370,12 +382,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* @param {string} query - The query that is typed in the field.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTypeQuery(query) {
|
||||
_onTypeQuery(query: string) {
|
||||
this.setState({
|
||||
fieldValue: query
|
||||
});
|
||||
|
||||
clearTimeout(this.searchTimeout);
|
||||
// @ts-ignore
|
||||
this.searchTimeout = setTimeout(() => {
|
||||
this.setState({
|
||||
searchInprogress: true
|
||||
@@ -391,7 +404,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* @param {string} query - The query to search for.
|
||||
* @returns {void}
|
||||
*/
|
||||
_performSearch(query) {
|
||||
_performSearch(query: string) {
|
||||
this._query(query).then(results => {
|
||||
this.setState({
|
||||
selectableItems: _.sortBy(results, [ 'name', 'number' ])
|
||||
@@ -404,19 +417,15 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
_query: (string) => Promise<Array<Object>>;
|
||||
|
||||
_renderInvitedItem: Object => ReactElement | null;
|
||||
|
||||
/**
|
||||
* Renders a single item in the invited {@code FlatList}.
|
||||
*
|
||||
* @param {Object} flatListItem - An item of the data array of the
|
||||
* {@code FlatList}.
|
||||
* @param {number} index - The index of the currently rendered item.
|
||||
* @returns {?React$Element<any>}
|
||||
* @returns {ReactElement<any>}
|
||||
*/
|
||||
_renderInvitedItem(flatListItem, index): ReactElement | null {
|
||||
_renderInvitedItem(flatListItem: any, index: number): ReactElement | null {
|
||||
const { item } = flatListItem;
|
||||
const renderableItem = this._getRenderableItem(flatListItem);
|
||||
|
||||
@@ -424,14 +433,14 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
<TouchableOpacity onPress = { this._onPressItem(item) } >
|
||||
<View
|
||||
pointerEvents = 'box-only'
|
||||
style = { styles.itemWrapper }>
|
||||
style = { styles.itemWrapper as ViewStyle }>
|
||||
<AvatarListItem
|
||||
avatarOnly = { true }
|
||||
avatarSize = { AVATAR_SIZE }
|
||||
avatarStatus = { item.status }
|
||||
avatarStyle = { styles.avatar }
|
||||
avatarTextStyle = { styles.avatarText }
|
||||
item = { renderableItem }
|
||||
item = { renderableItem as any }
|
||||
key = { index }
|
||||
linesStyle = { styles.itemLinesStyle }
|
||||
titleStyle = { styles.itemText } />
|
||||
@@ -443,18 +452,17 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_renderItem: Object => ReactElement | null;
|
||||
|
||||
/**
|
||||
* Renders a single item in the search result {@code FlatList}.
|
||||
*
|
||||
* @param {Object} flatListItem - An item of the data array of the
|
||||
* {@code FlatList}.
|
||||
* @param {number} index - The index of the currently rendered item.
|
||||
* @returns {?React$Element<*>}
|
||||
* @returns {?ReactElement<*>}
|
||||
*/
|
||||
_renderItem(flatListItem, index): ReactElement | null {
|
||||
_renderItem(flatListItem: any, index: number): ReactElement | null {
|
||||
const { item } = flatListItem;
|
||||
// @ts-ignore
|
||||
const { inviteItems } = this.state;
|
||||
let selected = false;
|
||||
const renderableItem = this._getRenderableItem(flatListItem);
|
||||
@@ -480,13 +488,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
<TouchableOpacity onPress = { this._onPressItem(item) } >
|
||||
<View
|
||||
pointerEvents = 'box-only'
|
||||
style = { styles.itemWrapper }>
|
||||
style = { styles.itemWrapper as ViewStyle }>
|
||||
<AvatarListItem
|
||||
avatarSize = { AVATAR_SIZE }
|
||||
avatarStatus = { item.status }
|
||||
avatarStyle = { styles.avatar }
|
||||
avatarTextStyle = { styles.avatarText }
|
||||
item = { renderableItem }
|
||||
item = { renderableItem as any }
|
||||
key = { index }
|
||||
linesStyle = { styles.itemLinesStyle }
|
||||
titleStyle = { styles.itemText } />
|
||||
@@ -498,12 +506,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_renderSeparator: () => ReactElement | null;
|
||||
|
||||
/**
|
||||
* Renders the item separator.
|
||||
*
|
||||
* @returns {?React$Element<*>}
|
||||
* @returns {?ReactElement<*>}
|
||||
*/
|
||||
_renderSeparator() {
|
||||
return (
|
||||
@@ -511,19 +517,16 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_renderShareMeetingButton: () => ReactElement;
|
||||
|
||||
/**
|
||||
* Renders a button to share the meeting info.
|
||||
*
|
||||
* @returns {React#Element<*>}
|
||||
*/
|
||||
_renderShareMeetingButton() {
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
styles.bottomBar,
|
||||
styles.bottomBar as ViewStyle,
|
||||
this.state.bottomPadding ? styles.extraBarPadding : null
|
||||
] }>
|
||||
<TouchableOpacity
|
||||
@@ -536,15 +539,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_renderIcon: () => ReactElement;
|
||||
|
||||
/**
|
||||
* Renders an icon.
|
||||
*
|
||||
* @returns {React#Element<*>}
|
||||
*/
|
||||
_renderIcon() {
|
||||
|
||||
if (this.state.searchInprogress) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
@@ -570,6 +570,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_showFailedInviteAlert() {
|
||||
// @ts-ignore
|
||||
this.props.dispatch(openDialog(AlertDialog, {
|
||||
contentKey: {
|
||||
key: 'inviteDialog.alertText'
|
||||
@@ -586,10 +587,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
||||
* _isVisible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state)
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default translate(connect(_mapStateToProps)(AddPeopleDialog));
|
||||
@@ -113,5 +113,9 @@ export default {
|
||||
left: AVATAR_SIZE / -3,
|
||||
position: 'relative',
|
||||
top: AVATAR_SIZE / -3
|
||||
},
|
||||
|
||||
sendBtn: {
|
||||
marginRight: BaseTheme.spacing[2]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
import { StatusCode } from '../../../../base/util/uri';
|
||||
import { isDynamicBrandingDataLoaded } from '../../../../dynamic-branding/functions.any';
|
||||
import { getActiveSession } from '../../../../recording/functions';
|
||||
import { updateDialInNumbers } from '../../../actions';
|
||||
import { updateDialInNumbers } from '../../../actions.web';
|
||||
import {
|
||||
_getDefaultPhoneNumber,
|
||||
getInviteText,
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
import logger from './logger';
|
||||
import { IInvitee } from './types';
|
||||
|
||||
declare let $: any;
|
||||
|
||||
export const sharingFeatures = {
|
||||
email: 'email',
|
||||
@@ -50,11 +49,16 @@ export function checkDialNumber(
|
||||
): Promise<{ allow?: boolean; country?: string; phone?: string; }> {
|
||||
const fullUrl = `${dialOutAuthUrl}?phone=${dialNumber}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
$.getJSON(fullUrl)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
return new Promise((resolve, reject) =>
|
||||
fetch(fullUrl)
|
||||
.then(res => {
|
||||
if (res.ok) {
|
||||
resolve(res.json());
|
||||
} else {
|
||||
reject(new Error('Request not successful!'));
|
||||
}
|
||||
})
|
||||
.catch(reject));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import { GestureResponderEvent } from 'react-native';
|
||||
|
||||
import { StyleType } from '../../../base/styles/functions.native';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import IconButton from '../../../base/ui/components/native/IconButton';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
|
||||
import { navigationStyles } from './styles';
|
||||
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
@@ -29,45 +31,61 @@ interface IProps {
|
||||
*/
|
||||
src?: any;
|
||||
|
||||
/**
|
||||
* Style of the button.
|
||||
*/
|
||||
style?: StyleType;
|
||||
|
||||
/**
|
||||
* Header has two actions.
|
||||
*/
|
||||
twoActions?: boolean;
|
||||
}
|
||||
|
||||
const HeaderNavigationButton
|
||||
= ({
|
||||
disabled,
|
||||
label,
|
||||
onPress,
|
||||
src,
|
||||
twoActions
|
||||
}: IProps) =>
|
||||
(
|
||||
<>
|
||||
{
|
||||
src ? (
|
||||
<IconButton
|
||||
onPress = { onPress }
|
||||
size = { 24 }
|
||||
src = { src }
|
||||
style = { navigationStyles.headerNavigationButton } />
|
||||
) : (
|
||||
<Button
|
||||
disabled = { disabled }
|
||||
labelKey = { label }
|
||||
labelStyle = {
|
||||
twoActions
|
||||
? navigationStyles.headerNavigationButtonLabelBold
|
||||
: navigationStyles.headerNavigationButtonLabel
|
||||
}
|
||||
onClick = { onPress }
|
||||
style = { navigationStyles.headerNavigationButton }
|
||||
type = { BUTTON_TYPES.TERTIARY }
|
||||
useRippleColor = { false } />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const HeaderNavigationButton = ({ disabled, label, onPress, src, style, twoActions }: IProps) => {
|
||||
|
||||
let btnStyle;
|
||||
let labelStyle;
|
||||
|
||||
if (disabled) {
|
||||
btnStyle = navigationStyles.headerNavigationButtonDisabled;
|
||||
labelStyle = twoActions
|
||||
? navigationStyles.headerNavigationButtonLabelBoldDisabled
|
||||
: navigationStyles.headerNavigationButtonLabelDisabled;
|
||||
} else {
|
||||
btnStyle = navigationStyles.headerNavigationButton;
|
||||
labelStyle = twoActions
|
||||
? navigationStyles.headerNavigationButtonLabelBold
|
||||
: navigationStyles.headerNavigationButtonLabel;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
src ? (
|
||||
<IconButton
|
||||
onPress = { onPress }
|
||||
size = { 24 }
|
||||
src = { src }
|
||||
style = { [
|
||||
navigationStyles.headerNavigationButton,
|
||||
style
|
||||
] } />
|
||||
) : (
|
||||
<Button
|
||||
disabled = { disabled }
|
||||
labelKey = { label }
|
||||
labelStyle = { labelStyle }
|
||||
onClick = { onPress }
|
||||
style = { [
|
||||
btnStyle,
|
||||
style
|
||||
] }
|
||||
type = { BUTTON_TYPES.TERTIARY }
|
||||
useRippleColor = { false } />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderNavigationButton;
|
||||
|
||||
@@ -46,15 +46,31 @@ export const navigationStyles = {
|
||||
marginLeft: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
headerNavigationButtonDisabled: {
|
||||
backgroundColor: 'transparent',
|
||||
marginLeft: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
headerNavigationButtonLabel: {
|
||||
...headerNavigationButtonLabel
|
||||
},
|
||||
|
||||
headerNavigationButtonLabelDisabled: {
|
||||
...headerNavigationButtonLabel,
|
||||
color: BaseTheme.palette.text03
|
||||
},
|
||||
|
||||
headerNavigationButtonLabelBold: {
|
||||
...headerNavigationButtonLabel,
|
||||
...BaseTheme.typography.bodyShortRegularLarge
|
||||
},
|
||||
|
||||
headerNavigationButtonLabelBoldDisabled: {
|
||||
...headerNavigationButtonLabel,
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
color: BaseTheme.palette.text03
|
||||
},
|
||||
|
||||
unreadCounterContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
|
||||
@@ -10,18 +10,25 @@ import { translate } from '../../../base/i18n/functions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconAddUser } from '../../../base/icons/svg';
|
||||
import {
|
||||
addPeopleFeatureControl,
|
||||
getLocalParticipant,
|
||||
getParticipantCountWithFake,
|
||||
getRemoteParticipants
|
||||
getRemoteParticipants,
|
||||
setShareDialogVisiblity
|
||||
} from '../../../base/participants/functions';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { getBreakoutRooms, getCurrentRoomId } from '../../../breakout-rooms/functions';
|
||||
import {
|
||||
getBreakoutRooms,
|
||||
getCurrentRoomId
|
||||
} from '../../../breakout-rooms/functions';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import { toggleShareDialog } from '../../../share-room/actions';
|
||||
import { getInviteOthersControl } from '../../../share-room/functions';
|
||||
import { participantMatchesSearch, shouldRenderInviteButton } from '../../functions';
|
||||
import {
|
||||
participantMatchesSearch,
|
||||
shouldRenderInviteButton
|
||||
} from '../../functions';
|
||||
|
||||
// @ts-ignore
|
||||
import CollapsibleList from './CollapsibleList';
|
||||
@@ -43,6 +50,11 @@ interface IProps extends WithTranslation {
|
||||
*/
|
||||
_inviteOthersControl: any;
|
||||
|
||||
/**
|
||||
* Checks if add-people feature is enabled.
|
||||
*/
|
||||
_isAddPeopleFeatureEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The local participant.
|
||||
*/
|
||||
@@ -139,8 +151,11 @@ class MeetingParticipantList extends PureComponent<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onInvite() {
|
||||
this.props.dispatch(toggleShareDialog(true));
|
||||
this.props.dispatch(doInvitePeople());
|
||||
const { _isAddPeopleFeatureEnabled, dispatch } = this.props;
|
||||
|
||||
setShareDialogVisiblity(_isAddPeopleFeatureEnabled, dispatch);
|
||||
|
||||
dispatch(doInvitePeople());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,6 +294,7 @@ function _mapStateToProps(state: IReduxState): Object {
|
||||
const { remoteParticipants } = state['features/filmstrip'];
|
||||
const { shareDialogVisible } = state['features/share-room'];
|
||||
const _inviteOthersControl = getInviteOthersControl(state);
|
||||
const _isAddPeopleFeatureEnabled = addPeopleFeatureControl(state);
|
||||
const _showInviteButton = shouldRenderInviteButton(state);
|
||||
const _remoteParticipants = getRemoteParticipants(state);
|
||||
const currentRoomId = getCurrentRoomId(state);
|
||||
@@ -286,6 +302,7 @@ function _mapStateToProps(state: IReduxState): Object {
|
||||
|
||||
return {
|
||||
_currentRoom,
|
||||
_isAddPeopleFeatureEnabled,
|
||||
_inviteOthersControl,
|
||||
_participantsCount,
|
||||
_remoteParticipants,
|
||||
|
||||
Reference in New Issue
Block a user