2019-02-26 10:41:57 +00:00
|
|
|
// @flow
|
|
|
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
import React from 'react';
|
|
|
|
import {
|
|
|
|
ActivityIndicator,
|
|
|
|
FlatList,
|
|
|
|
TouchableOpacity,
|
|
|
|
View
|
|
|
|
} from 'react-native';
|
2022-02-11 09:49:49 +00:00
|
|
|
import { withTheme } from 'react-native-paper';
|
2019-02-26 10:41:57 +00:00
|
|
|
|
2019-07-10 15:07:16 +00:00
|
|
|
import { AlertDialog, openDialog } from '../../../../base/dialog';
|
2019-02-26 10:41:57 +00:00
|
|
|
import { translate } from '../../../../base/i18n';
|
2020-04-01 12:13:51 +00:00
|
|
|
import {
|
|
|
|
Icon,
|
|
|
|
IconCancelSelection,
|
|
|
|
IconCheck,
|
|
|
|
IconPhone,
|
|
|
|
IconSearch,
|
|
|
|
IconShare
|
|
|
|
} from '../../../../base/icons';
|
2021-10-20 19:29:21 +00:00
|
|
|
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
2019-02-26 10:41:57 +00:00
|
|
|
import {
|
|
|
|
AvatarListItem,
|
|
|
|
type Item
|
|
|
|
} from '../../../../base/react';
|
2019-03-21 16:38:29 +00:00
|
|
|
import { connect } from '../../../../base/redux';
|
2022-02-11 09:49:49 +00:00
|
|
|
import HeaderNavigationButton
|
|
|
|
from '../../../../mobile/navigation/components/HeaderNavigationButton';
|
2021-10-21 11:58:44 +00:00
|
|
|
import ClearableInput from '../../../../participants-pane/components/native/ClearableInput';
|
2020-04-01 12:13:51 +00:00
|
|
|
import { beginShareRoom } from '../../../../share-room';
|
2021-10-20 19:29:21 +00:00
|
|
|
import { INVITE_TYPES } from '../../../constants';
|
2019-02-26 10:41:57 +00:00
|
|
|
import AbstractAddPeopleDialog, {
|
|
|
|
type Props as AbstractProps,
|
|
|
|
type State as AbstractState,
|
|
|
|
_mapStateToProps as _abstractMapStateToProps
|
|
|
|
} from '../AbstractAddPeopleDialog';
|
|
|
|
|
|
|
|
import styles, {
|
|
|
|
AVATAR_SIZE,
|
|
|
|
DARK_GREY
|
|
|
|
} from './styles';
|
|
|
|
|
|
|
|
type Props = AbstractProps & {
|
|
|
|
|
2020-04-01 12:13:51 +00:00
|
|
|
/**
|
2021-10-20 19:29:21 +00:00
|
|
|
* True if the invite dialog should be open, false otherwise.
|
2020-04-01 12:13:51 +00:00
|
|
|
*/
|
2021-10-20 19:29:21 +00:00
|
|
|
_isVisible: boolean,
|
2020-04-01 12:13:51 +00:00
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
/**
|
2021-11-04 21:10:43 +00:00
|
|
|
* Default prop for navigation between screen components(React Navigation).
|
2019-02-26 10:41:57 +00:00
|
|
|
*/
|
2021-10-20 19:29:21 +00:00
|
|
|
navigation: Object,
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function used to translate i18n labels.
|
|
|
|
*/
|
2021-10-20 19:29:21 +00:00
|
|
|
t: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Theme used for styles.
|
|
|
|
*/
|
|
|
|
theme: Object
|
2019-02-26 10:41:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
type State = AbstractState & {
|
|
|
|
|
2020-04-01 15:06:35 +00:00
|
|
|
/**
|
|
|
|
* Boolean to show if an extra padding needs to be added to the bottom bar.
|
|
|
|
*/
|
|
|
|
bottomPadding: boolean,
|
|
|
|
|
2019-06-27 10:35:50 +00:00
|
|
|
/**
|
|
|
|
* State variable to keep track of the search field value.
|
|
|
|
*/
|
|
|
|
fieldValue: string,
|
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
/**
|
|
|
|
* True if a search is in progress, false otherwise.
|
|
|
|
*/
|
|
|
|
searchInprogress: boolean,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of items that are selectable on this dialog. This is usually
|
|
|
|
* populated by an async search.
|
|
|
|
*/
|
|
|
|
selectableItems: Array<Object>
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a special dialog to invite people from a directory service.
|
|
|
|
*/
|
|
|
|
class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
|
|
|
/**
|
|
|
|
* Default state object to reset the state to when needed.
|
|
|
|
*/
|
|
|
|
defaultState = {
|
|
|
|
addToCallError: false,
|
|
|
|
addToCallInProgress: false,
|
2020-04-01 15:06:35 +00:00
|
|
|
bottomPadding: false,
|
2019-06-27 10:35:50 +00:00
|
|
|
fieldValue: '',
|
2019-02-26 10:41:57 +00:00
|
|
|
inviteItems: [],
|
|
|
|
searchInprogress: false,
|
|
|
|
selectableItems: []
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TimeoutID to delay the search for the time the user is probably typing.
|
|
|
|
*/
|
|
|
|
searchTimeout: TimeoutID;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contrustor of the component.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = this.defaultState;
|
|
|
|
|
|
|
|
this._keyExtractor = this._keyExtractor.bind(this);
|
2019-08-28 10:25:51 +00:00
|
|
|
this._renderInvitedItem = this._renderInvitedItem.bind(this);
|
2019-02-26 10:41:57 +00:00
|
|
|
this._renderItem = this._renderItem.bind(this);
|
|
|
|
this._renderSeparator = this._renderSeparator.bind(this);
|
2019-06-27 10:35:50 +00:00
|
|
|
this._onClearField = this._onClearField.bind(this);
|
2019-02-26 10:41:57 +00:00
|
|
|
this._onInvite = this._onInvite.bind(this);
|
|
|
|
this._onPressItem = this._onPressItem.bind(this);
|
2020-04-01 12:13:51 +00:00
|
|
|
this._onShareMeeting = this._onShareMeeting.bind(this);
|
2019-02-26 10:41:57 +00:00
|
|
|
this._onTypeQuery = this._onTypeQuery.bind(this);
|
2020-04-06 15:26:20 +00:00
|
|
|
this._renderShareMeetingButton = this._renderShareMeetingButton.bind(this);
|
2019-02-26 10:41:57 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 19:29:21 +00:00
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#componentDidMount()}. Invoked
|
|
|
|
* immediately after this component is mounted.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
componentDidMount() {
|
2022-02-11 09:49:49 +00:00
|
|
|
const { navigation, t } = this.props;
|
2021-10-20 19:29:21 +00:00
|
|
|
|
|
|
|
navigation.setOptions({
|
|
|
|
headerRight: () => (
|
2022-02-11 09:49:49 +00:00
|
|
|
<HeaderNavigationButton
|
2021-10-20 19:29:21 +00:00
|
|
|
disabled = { this._isAddDisabled() }
|
2022-02-11 09:49:49 +00:00
|
|
|
label = { t('inviteDialog.send') }
|
|
|
|
twoActions = { true } />
|
2021-10-20 19:29:21 +00:00
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
/**
|
|
|
|
* Implements {@code Component#componentDidUpdate}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
componentDidUpdate(prevProps) {
|
2022-02-11 09:49:49 +00:00
|
|
|
const { navigation, t } = this.props;
|
2021-10-20 19:29:21 +00:00
|
|
|
|
|
|
|
navigation.setOptions({
|
|
|
|
// eslint-disable-next-line react/no-multi-comp
|
|
|
|
headerRight: () => (
|
2022-02-11 09:49:49 +00:00
|
|
|
<HeaderNavigationButton
|
2021-10-20 19:29:21 +00:00
|
|
|
disabled = { this._isAddDisabled() }
|
2022-02-11 09:49:49 +00:00
|
|
|
label = { t('inviteDialog.send') }
|
2021-10-20 19:29:21 +00:00
|
|
|
onPress = { this._onInvite }
|
2022-02-11 09:49:49 +00:00
|
|
|
twoActions = { true } />
|
2021-10-20 19:29:21 +00:00
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
if (prevProps._isVisible !== this.props._isVisible) {
|
|
|
|
// Clear state
|
|
|
|
this._clearState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements {@code Component#render}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
const {
|
|
|
|
_addPeopleEnabled,
|
2020-04-06 15:26:20 +00:00
|
|
|
_dialOutEnabled
|
2019-02-26 10:41:57 +00:00
|
|
|
} = this.props;
|
2019-08-28 10:25:51 +00:00
|
|
|
const { inviteItems, selectableItems } = this.state;
|
2021-10-20 19:29:21 +00:00
|
|
|
const { theme } = this.props;
|
|
|
|
const { palette } = theme;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
let placeholderKey = 'searchPlaceholder';
|
|
|
|
|
|
|
|
if (!_addPeopleEnabled) {
|
|
|
|
placeholderKey = 'searchCallOnlyPlaceholder';
|
|
|
|
} else if (!_dialOutEnabled) {
|
|
|
|
placeholderKey = 'searchPeopleOnlyPlaceholder';
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2021-10-20 19:29:21 +00:00
|
|
|
<JitsiScreen
|
2020-04-06 15:26:20 +00:00
|
|
|
footerComponent = { this._renderShareMeetingButton }
|
2021-10-20 19:29:21 +00:00
|
|
|
style = { styles.addPeopleContainer }>
|
2021-10-21 11:58:44 +00:00
|
|
|
<ClearableInput
|
|
|
|
autoFocus = { false }
|
|
|
|
customStyles = {{
|
|
|
|
wrapper: styles.searchFieldWrapper,
|
|
|
|
input: styles.searchField,
|
|
|
|
clearButton: styles.clearButton,
|
|
|
|
clearIcon: styles.clearIcon
|
|
|
|
}}
|
|
|
|
onChange = { this._onTypeQuery }
|
|
|
|
placeholder = { this.props.t(`inviteDialog.${placeholderKey}`) }
|
|
|
|
placeholderColor = { palette.text04 }
|
|
|
|
prefixComponent = { <View style = { styles.searchIconWrapper }>
|
|
|
|
{this.state.searchInprogress
|
2020-04-06 15:26:20 +00:00
|
|
|
? <ActivityIndicator
|
|
|
|
color = { DARK_GREY }
|
|
|
|
size = 'small' />
|
|
|
|
: <Icon
|
|
|
|
src = { IconSearch }
|
|
|
|
style = { styles.searchIcon } />}
|
2021-10-21 11:58:44 +00:00
|
|
|
</View> }
|
|
|
|
value = { this.state.fieldValue } />
|
2020-04-06 15:26:20 +00:00
|
|
|
{ Boolean(inviteItems.length) && <View style = { styles.invitedList }>
|
|
|
|
<FlatList
|
|
|
|
data = { inviteItems }
|
|
|
|
horizontal = { true }
|
|
|
|
keyExtractor = { this._keyExtractor }
|
|
|
|
keyboardShouldPersistTaps = 'always'
|
|
|
|
renderItem = { this._renderInvitedItem } />
|
|
|
|
</View> }
|
|
|
|
<View style = { styles.resultList }>
|
|
|
|
<FlatList
|
|
|
|
ItemSeparatorComponent = { this._renderSeparator }
|
|
|
|
data = { selectableItems }
|
|
|
|
extraData = { inviteItems }
|
|
|
|
keyExtractor = { this._keyExtractor }
|
|
|
|
keyboardShouldPersistTaps = 'always'
|
|
|
|
renderItem = { this._renderItem } />
|
|
|
|
</View>
|
2021-10-20 19:29:21 +00:00
|
|
|
</JitsiScreen>
|
2019-02-26 10:41:57 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clears the dialog content.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_clearState() {
|
|
|
|
this.setState(this.defaultState);
|
|
|
|
}
|
|
|
|
|
2019-08-28 10:25:51 +00:00
|
|
|
/**
|
|
|
|
* Returns an object capable of being rendered by an {@code AvatarListItem}.
|
|
|
|
*
|
|
|
|
* @param {Object} flatListItem - An item of the data array of the {@code FlatList}.
|
|
|
|
* @returns {?Object}
|
|
|
|
*/
|
|
|
|
_getRenderableItem(flatListItem) {
|
|
|
|
const { item } = flatListItem;
|
|
|
|
|
|
|
|
switch (item.type) {
|
2021-05-11 08:51:02 +00:00
|
|
|
case INVITE_TYPES.PHONE:
|
2019-08-28 10:25:51 +00:00
|
|
|
return {
|
2019-08-30 16:39:06 +00:00
|
|
|
avatar: IconPhone,
|
2019-08-28 10:25:51 +00:00
|
|
|
key: item.number,
|
|
|
|
title: item.number
|
|
|
|
};
|
2021-05-11 08:51:02 +00:00
|
|
|
case INVITE_TYPES.USER:
|
2019-08-28 10:25:51 +00:00
|
|
|
return {
|
|
|
|
avatar: item.avatar,
|
|
|
|
key: item.id || item.user_id,
|
|
|
|
title: item.name
|
|
|
|
};
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_invite: Array<Object> => Promise<Array<Object>>;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
_isAddDisabled: () => boolean;
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_keyExtractor: Object => string;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Key extractor for the flatlist.
|
|
|
|
*
|
|
|
|
* @param {Object} item - The flatlist item that we need the key to be
|
|
|
|
* generated for.
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
_keyExtractor(item) {
|
2021-05-11 08:51:02 +00:00
|
|
|
return item.type === INVITE_TYPES.USER ? item.id || item.user_id : item.number;
|
2019-02-26 10:41:57 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_onClearField: () => void;
|
2019-06-27 10:35:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback to clear the text field.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onClearField() {
|
|
|
|
this.setState({
|
|
|
|
fieldValue: ''
|
|
|
|
});
|
|
|
|
|
|
|
|
// Clear search results
|
|
|
|
this._onTypeQuery('');
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_onInvite: () => void;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Invites the selected entries.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onInvite() {
|
|
|
|
this._invite(this.state.inviteItems)
|
|
|
|
.then(invitesLeftToSend => {
|
|
|
|
if (invitesLeftToSend.length) {
|
|
|
|
this.setState({
|
|
|
|
inviteItems: invitesLeftToSend
|
|
|
|
});
|
|
|
|
this._showFailedInviteAlert();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_onPressItem: Item => Function;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
2021-03-16 15:59:33 +00:00
|
|
|
* Function to prepare a callback for the onPress event of the touchable.
|
2019-02-26 10:41:57 +00:00
|
|
|
*
|
|
|
|
* @param {Item} item - The item on which onPress was invoked.
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
_onPressItem(item) {
|
|
|
|
return () => {
|
|
|
|
const { inviteItems } = this.state;
|
2021-05-11 08:51:02 +00:00
|
|
|
const finderKey = item.type === INVITE_TYPES.PHONE ? 'number' : 'user_id';
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
if (inviteItems.find(
|
|
|
|
_.matchesProperty(finderKey, item[finderKey]))) {
|
|
|
|
// Item is already selected, need to unselect it.
|
|
|
|
this.setState({
|
|
|
|
inviteItems: inviteItems.filter(
|
|
|
|
element => item[finderKey] !== element[finderKey])
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Item is not selected yet, need to add to the list.
|
2019-08-28 10:25:51 +00:00
|
|
|
const items: Array<Object> = inviteItems.concat(item);
|
2019-03-19 15:42:25 +00:00
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
this.setState({
|
2019-06-05 12:29:10 +00:00
|
|
|
inviteItems: _.sortBy(items, [ 'name', 'number' ])
|
2019-02-26 10:41:57 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_onShareMeeting: () => void;
|
2020-04-01 12:13:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Shows the system share sheet to share the meeting information.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onShareMeeting() {
|
|
|
|
if (this.state.inviteItems.length > 0) {
|
|
|
|
// The use probably intended to invite people.
|
|
|
|
this._onInvite();
|
|
|
|
} else {
|
|
|
|
this.props.dispatch(beginShareRoom());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_onTypeQuery: string => void;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles the typing event of the text field on the dialog and performs the
|
|
|
|
* search.
|
|
|
|
*
|
|
|
|
* @param {string} query - The query that is typed in the field.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onTypeQuery(query) {
|
2019-06-27 10:35:50 +00:00
|
|
|
this.setState({
|
|
|
|
fieldValue: query
|
|
|
|
});
|
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
clearTimeout(this.searchTimeout);
|
|
|
|
this.searchTimeout = setTimeout(() => {
|
|
|
|
this.setState({
|
|
|
|
searchInprogress: true
|
|
|
|
}, () => {
|
|
|
|
this._performSearch(query);
|
|
|
|
});
|
|
|
|
}, 500);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Performs the actual search.
|
|
|
|
*
|
|
|
|
* @param {string} query - The query to search for.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_performSearch(query) {
|
|
|
|
this._query(query).then(results => {
|
|
|
|
this.setState({
|
2019-08-28 10:25:51 +00:00
|
|
|
selectableItems: _.sortBy(results, [ 'name', 'number' ])
|
2019-02-26 10:41:57 +00:00
|
|
|
});
|
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
this.setState({
|
|
|
|
searchInprogress: false
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_query: (string) => Promise<Array<Object>>;
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_renderInvitedItem: Object => React$Element<any> | null;
|
2019-08-28 10:25:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2019-09-25 13:01:17 +00:00
|
|
|
* @returns {?React$Element<any>}
|
2019-08-28 10:25:51 +00:00
|
|
|
*/
|
2019-09-25 13:01:17 +00:00
|
|
|
_renderInvitedItem(flatListItem, index): React$Element<any> | null {
|
2019-08-28 10:25:51 +00:00
|
|
|
const { item } = flatListItem;
|
|
|
|
const renderableItem = this._getRenderableItem(flatListItem);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<TouchableOpacity onPress = { this._onPressItem(item) } >
|
|
|
|
<View
|
|
|
|
pointerEvents = 'box-only'
|
|
|
|
style = { styles.itemWrapper }>
|
|
|
|
<AvatarListItem
|
|
|
|
avatarOnly = { true }
|
|
|
|
avatarSize = { AVATAR_SIZE }
|
2019-12-06 15:02:51 +00:00
|
|
|
avatarStatus = { item.status }
|
2019-08-28 10:25:51 +00:00
|
|
|
avatarStyle = { styles.avatar }
|
|
|
|
avatarTextStyle = { styles.avatarText }
|
|
|
|
item = { renderableItem }
|
|
|
|
key = { index }
|
|
|
|
linesStyle = { styles.itemLinesStyle }
|
|
|
|
titleStyle = { styles.itemText } />
|
|
|
|
<Icon
|
2019-08-30 16:39:06 +00:00
|
|
|
src = { IconCancelSelection }
|
2019-08-28 10:25:51 +00:00
|
|
|
style = { styles.unselectIcon } />
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_renderItem: Object => React$Element<any> | null;
|
2019-08-28 10:25:51 +00:00
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
/**
|
2019-08-28 10:25:51 +00:00
|
|
|
* Renders a single item in the search result {@code FlatList}.
|
2019-02-26 10:41:57 +00:00
|
|
|
*
|
|
|
|
* @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<*>}
|
|
|
|
*/
|
2019-09-25 13:01:17 +00:00
|
|
|
_renderItem(flatListItem, index): React$Element<any> | null {
|
2019-02-26 10:41:57 +00:00
|
|
|
const { item } = flatListItem;
|
|
|
|
const { inviteItems } = this.state;
|
|
|
|
let selected = false;
|
2019-08-28 10:25:51 +00:00
|
|
|
const renderableItem = this._getRenderableItem(flatListItem);
|
|
|
|
|
|
|
|
if (!renderableItem) {
|
|
|
|
return null;
|
|
|
|
}
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
switch (item.type) {
|
2021-05-11 08:51:02 +00:00
|
|
|
case INVITE_TYPES.PHONE:
|
2019-08-28 10:25:51 +00:00
|
|
|
selected = inviteItems.find(_.matchesProperty('number', item.number));
|
2019-02-26 10:41:57 +00:00
|
|
|
break;
|
2021-05-11 08:51:02 +00:00
|
|
|
case INVITE_TYPES.USER:
|
2019-08-28 10:25:51 +00:00
|
|
|
selected = item.id
|
|
|
|
? inviteItems.find(_.matchesProperty('id', item.id))
|
|
|
|
: inviteItems.find(_.matchesProperty('user_id', item.user_id));
|
2019-02-26 10:41:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<TouchableOpacity onPress = { this._onPressItem(item) } >
|
|
|
|
<View
|
|
|
|
pointerEvents = 'box-only'
|
|
|
|
style = { styles.itemWrapper }>
|
|
|
|
<AvatarListItem
|
|
|
|
avatarSize = { AVATAR_SIZE }
|
2019-12-06 15:02:51 +00:00
|
|
|
avatarStatus = { item.status }
|
2019-02-26 10:41:57 +00:00
|
|
|
avatarStyle = { styles.avatar }
|
|
|
|
avatarTextStyle = { styles.avatarText }
|
|
|
|
item = { renderableItem }
|
|
|
|
key = { index }
|
|
|
|
linesStyle = { styles.itemLinesStyle }
|
|
|
|
titleStyle = { styles.itemText } />
|
2019-08-28 10:25:51 +00:00
|
|
|
{ selected && <Icon
|
2019-08-30 16:39:06 +00:00
|
|
|
src = { IconCheck }
|
2019-08-28 10:25:51 +00:00
|
|
|
style = { styles.selectedIcon } /> }
|
2019-02-26 10:41:57 +00:00
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-04 21:10:43 +00:00
|
|
|
_renderSeparator: () => React$Element<*> | null;
|
2019-02-26 10:41:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders the item separator.
|
|
|
|
*
|
|
|
|
* @returns {?React$Element<*>}
|
|
|
|
*/
|
|
|
|
_renderSeparator() {
|
|
|
|
return (
|
|
|
|
<View style = { styles.separator } />
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-06 15:26:20 +00:00
|
|
|
_renderShareMeetingButton: () => React$Element<any>;
|
|
|
|
|
2020-04-01 12:13:51 +00:00
|
|
|
/**
|
|
|
|
* Renders a button to share the meeting info.
|
|
|
|
*
|
|
|
|
* @returns {React#Element<*>}
|
|
|
|
*/
|
|
|
|
_renderShareMeetingButton() {
|
|
|
|
|
|
|
|
return (
|
2021-10-20 19:29:21 +00:00
|
|
|
<View
|
2020-04-06 15:26:20 +00:00
|
|
|
style = { [
|
|
|
|
styles.bottomBar,
|
|
|
|
this.state.bottomPadding ? styles.extraBarPadding : null
|
|
|
|
] }>
|
|
|
|
<TouchableOpacity
|
|
|
|
onPress = { this._onShareMeeting }>
|
|
|
|
<Icon
|
|
|
|
src = { IconShare }
|
2021-10-20 19:29:21 +00:00
|
|
|
style = { styles.shareIcon } />
|
2020-04-06 15:26:20 +00:00
|
|
|
</TouchableOpacity>
|
2021-10-20 19:29:21 +00:00
|
|
|
</View>
|
2020-04-01 12:13:51 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-26 10:41:57 +00:00
|
|
|
/**
|
|
|
|
* Shows an alert telling the user that some invitees were failed to be
|
|
|
|
* invited.
|
|
|
|
*
|
|
|
|
* NOTE: We're using an Alert here because we're on a modal and it makes
|
|
|
|
* using our dialogs a tad more difficult.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_showFailedInviteAlert() {
|
2019-07-10 15:07:16 +00:00
|
|
|
this.props.dispatch(openDialog(AlertDialog, {
|
|
|
|
contentKey: {
|
|
|
|
key: 'inviteDialog.alertText'
|
|
|
|
}
|
|
|
|
}));
|
2019-02-26 10:41:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maps part of the Redux state to the props of this component.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The Redux state.
|
|
|
|
* @returns {{
|
|
|
|
* _isVisible: boolean
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
function _mapStateToProps(state: Object) {
|
|
|
|
return {
|
2021-10-20 19:29:21 +00:00
|
|
|
..._abstractMapStateToProps(state)
|
2019-02-26 10:41:57 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-10-20 19:29:21 +00:00
|
|
|
export default translate(connect(_mapStateToProps)(withTheme(AddPeopleDialog)));
|