2017-08-10 16:25:50 +00:00
|
|
|
import React from 'react';
|
2017-09-01 19:42:50 +00:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import PropTypes from 'prop-types';
|
2018-09-25 19:28:42 +00:00
|
|
|
import {
|
2019-01-29 19:52:56 +00:00
|
|
|
View, Text, Switch, ScrollView, TextInput, StyleSheet, FlatList
|
2018-09-25 19:28:42 +00:00
|
|
|
} from 'react-native';
|
2019-03-12 16:23:06 +00:00
|
|
|
import { SafeAreaView } from 'react-navigation';
|
2018-12-21 10:55:35 +00:00
|
|
|
import equal from 'deep-equal';
|
2018-04-03 16:24:59 +00:00
|
|
|
|
2018-04-24 19:34:03 +00:00
|
|
|
import Loading from '../containers/Loading';
|
2018-04-03 16:24:59 +00:00
|
|
|
import LoggedView from './View';
|
2018-09-25 19:28:42 +00:00
|
|
|
import { createChannelRequest as createChannelRequestAction } from '../actions/createChannel';
|
|
|
|
import { removeUser as removeUserAction } from '../actions/selectedUsers';
|
2018-08-31 18:13:30 +00:00
|
|
|
import sharedStyles from './Styles';
|
2017-09-01 20:00:31 +00:00
|
|
|
import KeyboardView from '../presentation/KeyboardView';
|
2018-04-24 19:34:03 +00:00
|
|
|
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
2018-06-01 17:38:13 +00:00
|
|
|
import I18n from '../i18n';
|
2018-08-31 18:13:30 +00:00
|
|
|
import UserItem from '../presentation/UserItem';
|
|
|
|
import { showErrorAlert } from '../utils/info';
|
2019-01-29 19:52:56 +00:00
|
|
|
import { isAndroid } from '../utils/deviceInfo';
|
2019-03-12 16:23:06 +00:00
|
|
|
import { CustomHeaderButtons, Item } from '../containers/HeaderButton';
|
|
|
|
import StatusBar from '../containers/StatusBar';
|
2019-03-29 19:36:07 +00:00
|
|
|
import { COLOR_TEXT_DESCRIPTION, COLOR_WHITE } from '../constants/colors';
|
2018-08-31 18:13:30 +00:00
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
2018-09-11 16:32:52 +00:00
|
|
|
backgroundColor: '#f7f8fa',
|
|
|
|
flex: 1
|
2018-08-31 18:13:30 +00:00
|
|
|
},
|
|
|
|
list: {
|
|
|
|
width: '100%',
|
2019-03-29 19:36:07 +00:00
|
|
|
backgroundColor: COLOR_WHITE
|
2018-08-31 18:13:30 +00:00
|
|
|
},
|
|
|
|
separator: {
|
|
|
|
marginLeft: 60
|
|
|
|
},
|
|
|
|
formSeparator: {
|
|
|
|
marginLeft: 15
|
|
|
|
},
|
|
|
|
input: {
|
|
|
|
height: 54,
|
|
|
|
paddingHorizontal: 18,
|
2019-03-29 19:36:07 +00:00
|
|
|
fontSize: 17,
|
|
|
|
...sharedStyles.textRegular,
|
|
|
|
...sharedStyles.textColorNormal,
|
|
|
|
backgroundColor: COLOR_WHITE
|
2018-08-31 18:13:30 +00:00
|
|
|
},
|
|
|
|
swithContainer: {
|
|
|
|
height: 54,
|
2019-03-29 19:36:07 +00:00
|
|
|
backgroundColor: COLOR_WHITE,
|
2018-08-31 18:13:30 +00:00
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
flexDirection: 'row',
|
|
|
|
paddingHorizontal: 18
|
|
|
|
},
|
|
|
|
label: {
|
2019-03-29 19:36:07 +00:00
|
|
|
fontSize: 17,
|
|
|
|
...sharedStyles.textMedium,
|
|
|
|
...sharedStyles.textColorNormal
|
2018-08-31 18:13:30 +00:00
|
|
|
},
|
|
|
|
invitedHeader: {
|
|
|
|
marginTop: 18,
|
|
|
|
marginHorizontal: 15,
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
alignItems: 'center'
|
|
|
|
},
|
|
|
|
invitedTitle: {
|
2019-03-29 19:36:07 +00:00
|
|
|
fontSize: 18,
|
|
|
|
...sharedStyles.textSemibold,
|
|
|
|
...sharedStyles.textColorNormal,
|
2018-08-31 18:13:30 +00:00
|
|
|
lineHeight: 41
|
|
|
|
},
|
|
|
|
invitedCount: {
|
2019-03-29 19:36:07 +00:00
|
|
|
fontSize: 14,
|
|
|
|
...sharedStyles.textRegular,
|
|
|
|
...sharedStyles.textColorDescription
|
2018-08-31 18:13:30 +00:00
|
|
|
}
|
|
|
|
});
|
2017-08-10 16:25:50 +00:00
|
|
|
|
2018-07-10 13:40:32 +00:00
|
|
|
@connect(state => ({
|
2018-09-11 16:32:52 +00:00
|
|
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
2018-10-23 21:39:48 +00:00
|
|
|
error: state.createChannel.error,
|
|
|
|
failure: state.createChannel.failure,
|
|
|
|
isFetching: state.createChannel.isFetching,
|
|
|
|
result: state.createChannel.result,
|
2019-02-07 19:58:20 +00:00
|
|
|
users: state.selectedUsers.users,
|
|
|
|
user: {
|
|
|
|
id: state.login.user && state.login.user.id,
|
|
|
|
token: state.login.user && state.login.user.token
|
|
|
|
}
|
2018-07-10 13:40:32 +00:00
|
|
|
}), dispatch => ({
|
2018-09-25 19:28:42 +00:00
|
|
|
create: data => dispatch(createChannelRequestAction(data)),
|
|
|
|
removeUser: user => dispatch(removeUserAction(user))
|
2018-07-10 13:40:32 +00:00
|
|
|
}))
|
|
|
|
/** @extends React.Component */
|
2018-04-03 16:24:59 +00:00
|
|
|
export default class CreateChannelView extends LoggedView {
|
2019-03-12 16:23:06 +00:00
|
|
|
static navigationOptions = ({ navigation }) => {
|
|
|
|
const submit = navigation.getParam('submit', () => {});
|
|
|
|
const showSubmit = navigation.getParam('showSubmit');
|
2018-11-14 21:42:03 +00:00
|
|
|
return {
|
2019-03-12 16:23:06 +00:00
|
|
|
title: I18n.t('Create_Channel'),
|
|
|
|
headerRight: (
|
|
|
|
showSubmit
|
|
|
|
? (
|
|
|
|
<CustomHeaderButtons>
|
|
|
|
<Item title={I18n.t('Create')} onPress={submit} testID='create-channel-submit' />
|
|
|
|
</CustomHeaderButtons>
|
|
|
|
)
|
|
|
|
: null
|
|
|
|
)
|
2018-11-14 21:42:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-09-01 19:42:50 +00:00
|
|
|
static propTypes = {
|
2019-03-12 16:23:06 +00:00
|
|
|
navigation: PropTypes.object,
|
2018-09-11 16:32:52 +00:00
|
|
|
baseUrl: PropTypes.string,
|
2018-04-10 13:03:54 +00:00
|
|
|
create: PropTypes.func.isRequired,
|
2018-08-31 18:13:30 +00:00
|
|
|
removeUser: PropTypes.func.isRequired,
|
2018-10-23 21:39:48 +00:00
|
|
|
error: PropTypes.object,
|
|
|
|
failure: PropTypes.bool,
|
|
|
|
isFetching: PropTypes.bool,
|
|
|
|
result: PropTypes.object,
|
2019-02-07 19:58:20 +00:00
|
|
|
users: PropTypes.array.isRequired,
|
|
|
|
user: PropTypes.shape({
|
|
|
|
id: PropTypes.string,
|
|
|
|
token: PropTypes.string
|
|
|
|
})
|
2017-09-25 13:15:28 +00:00
|
|
|
};
|
2017-08-10 16:25:50 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
2018-04-03 16:24:59 +00:00
|
|
|
super('CreateChannelView', props);
|
2018-04-10 13:03:54 +00:00
|
|
|
this.state = {
|
2017-08-10 16:25:50 +00:00
|
|
|
channelName: '',
|
2018-05-24 20:17:45 +00:00
|
|
|
type: true,
|
|
|
|
readOnly: false,
|
|
|
|
broadcast: false
|
2017-08-10 16:25:50 +00:00
|
|
|
};
|
2017-09-25 13:15:28 +00:00
|
|
|
}
|
|
|
|
|
2018-08-01 19:35:06 +00:00
|
|
|
componentDidMount() {
|
2019-03-12 16:23:06 +00:00
|
|
|
const { navigation } = this.props;
|
|
|
|
navigation.setParams({ submit: this.submit });
|
2018-11-14 21:42:03 +00:00
|
|
|
this.timeout = setTimeout(() => {
|
2018-08-01 19:35:06 +00:00
|
|
|
this.channelNameRef.focus();
|
|
|
|
}, 600);
|
|
|
|
}
|
|
|
|
|
2018-12-21 10:55:35 +00:00
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
|
|
const {
|
|
|
|
channelName, type, readOnly, broadcast
|
|
|
|
} = this.state;
|
|
|
|
const {
|
|
|
|
error, failure, isFetching, result, users
|
|
|
|
} = this.props;
|
|
|
|
if (nextState.channelName !== channelName) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (nextState.type !== type) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (nextState.readOnly !== readOnly) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (nextState.broadcast !== broadcast) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (nextProps.failure !== failure) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (nextProps.isFetching !== isFetching) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!equal(nextProps.error, error)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!equal(nextProps.result, result)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!equal(nextProps.users, users)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:13:30 +00:00
|
|
|
componentDidUpdate(prevProps) {
|
2018-10-23 21:39:48 +00:00
|
|
|
const {
|
2019-03-12 16:23:06 +00:00
|
|
|
isFetching, failure, error, result, navigation
|
2018-10-23 21:39:48 +00:00
|
|
|
} = this.props;
|
2018-09-25 19:28:42 +00:00
|
|
|
|
2018-10-23 21:39:48 +00:00
|
|
|
if (!isFetching && isFetching !== prevProps.isFetching) {
|
2019-03-12 16:23:06 +00:00
|
|
|
setTimeout(() => {
|
2018-10-23 21:39:48 +00:00
|
|
|
if (failure) {
|
|
|
|
const msg = error.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
|
|
|
|
showErrorAlert(msg);
|
|
|
|
} else {
|
2018-12-21 10:55:35 +00:00
|
|
|
const { type } = this.state;
|
|
|
|
const { rid, name } = result;
|
2019-03-12 16:23:06 +00:00
|
|
|
navigation.navigate('RoomView', { rid, name, t: type ? 'p' : 'c' });
|
2018-10-23 21:39:48 +00:00
|
|
|
}
|
2018-08-31 18:13:30 +00:00
|
|
|
}, 300);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-14 21:42:03 +00:00
|
|
|
componentWillUnmount() {
|
|
|
|
if (this.timeout) {
|
|
|
|
clearTimeout(this.timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:13:30 +00:00
|
|
|
onChangeText = (channelName) => {
|
2019-03-12 16:23:06 +00:00
|
|
|
const { navigation } = this.props;
|
|
|
|
navigation.setParams({ showSubmit: channelName.trim().length > 0 });
|
2018-08-31 18:13:30 +00:00
|
|
|
this.setState({ channelName });
|
|
|
|
}
|
|
|
|
|
2018-05-24 20:17:45 +00:00
|
|
|
submit = () => {
|
|
|
|
const {
|
|
|
|
channelName, type, readOnly, broadcast
|
|
|
|
} = this.state;
|
2018-10-23 21:39:48 +00:00
|
|
|
const { users: usersProps, isFetching, create } = this.props;
|
2018-09-25 19:28:42 +00:00
|
|
|
|
2018-10-23 21:39:48 +00:00
|
|
|
if (!channelName.trim() || isFetching) {
|
2018-09-25 19:28:42 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-09-25 13:15:28 +00:00
|
|
|
|
|
|
|
// transform users object into array of usernames
|
2018-09-25 19:28:42 +00:00
|
|
|
const users = usersProps.map(user => user.name);
|
2017-09-25 13:15:28 +00:00
|
|
|
|
|
|
|
// create channel
|
2018-09-25 19:28:42 +00:00
|
|
|
create({
|
2018-05-24 20:17:45 +00:00
|
|
|
name: channelName, users, type, readOnly, broadcast
|
|
|
|
});
|
2017-08-10 16:25:50 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 18:13:30 +00:00
|
|
|
removeUser = (user) => {
|
2018-09-25 19:28:42 +00:00
|
|
|
const { users, removeUser } = this.props;
|
|
|
|
if (users.length === 1) {
|
2018-08-31 18:13:30 +00:00
|
|
|
return;
|
2017-09-21 17:08:00 +00:00
|
|
|
}
|
2018-09-25 19:28:42 +00:00
|
|
|
removeUser(user);
|
2017-09-21 17:08:00 +00:00
|
|
|
}
|
|
|
|
|
2018-05-24 20:17:45 +00:00
|
|
|
renderSwitch = ({
|
2018-08-31 18:13:30 +00:00
|
|
|
id, value, label, onValueChange, disabled = false
|
2018-05-24 20:17:45 +00:00
|
|
|
}) => (
|
2018-08-31 18:13:30 +00:00
|
|
|
<View style={styles.swithContainer}>
|
|
|
|
<Text style={styles.label}>{I18n.t(label)}</Text>
|
|
|
|
<Switch
|
|
|
|
value={value}
|
|
|
|
onValueChange={onValueChange}
|
|
|
|
testID={`create-channel-${ id }`}
|
|
|
|
onTintColor='#2de0a5'
|
2019-01-29 19:52:56 +00:00
|
|
|
tintColor={isAndroid ? '#f5455c' : null}
|
2018-08-31 18:13:30 +00:00
|
|
|
disabled={disabled}
|
|
|
|
/>
|
2018-05-24 20:17:45 +00:00
|
|
|
</View>
|
2018-08-31 18:13:30 +00:00
|
|
|
)
|
2018-05-24 20:17:45 +00:00
|
|
|
|
|
|
|
renderType() {
|
|
|
|
const { type } = this.state;
|
|
|
|
return this.renderSwitch({
|
|
|
|
id: 'type',
|
|
|
|
value: type,
|
2018-08-31 18:13:30 +00:00
|
|
|
label: 'Private_Channel',
|
2018-05-24 20:17:45 +00:00
|
|
|
onValueChange: value => this.setState({ type: value })
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
renderReadOnly() {
|
|
|
|
const { readOnly, broadcast } = this.state;
|
|
|
|
return this.renderSwitch({
|
|
|
|
id: 'readonly',
|
|
|
|
value: readOnly,
|
2018-08-31 18:13:30 +00:00
|
|
|
label: 'Read_Only_Channel',
|
2018-05-24 20:17:45 +00:00
|
|
|
onValueChange: value => this.setState({ readOnly: value }),
|
|
|
|
disabled: broadcast
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
renderBroadcast() {
|
|
|
|
const { broadcast, readOnly } = this.state;
|
|
|
|
return this.renderSwitch({
|
|
|
|
id: 'broadcast',
|
|
|
|
value: broadcast,
|
2018-08-31 18:13:30 +00:00
|
|
|
label: 'Broadcast_Channel',
|
2018-05-24 20:17:45 +00:00
|
|
|
onValueChange: (value) => {
|
|
|
|
this.setState({
|
|
|
|
broadcast: value,
|
|
|
|
readOnly: value ? true : readOnly
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2017-09-21 17:08:00 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 18:13:30 +00:00
|
|
|
renderSeparator = () => <View style={[sharedStyles.separator, styles.separator]} />
|
|
|
|
|
|
|
|
renderFormSeparator = () => <View style={[sharedStyles.separator, styles.formSeparator]} />
|
|
|
|
|
2018-09-25 19:28:42 +00:00
|
|
|
renderItem = ({ item }) => {
|
2019-02-07 19:58:20 +00:00
|
|
|
const { baseUrl, user } = this.props;
|
2018-08-31 18:13:30 +00:00
|
|
|
|
2018-09-25 19:28:42 +00:00
|
|
|
return (
|
|
|
|
<UserItem
|
|
|
|
name={item.fname}
|
|
|
|
username={item.name}
|
|
|
|
onPress={() => this.removeUser(item)}
|
|
|
|
testID={`create-channel-view-item-${ item.name }`}
|
|
|
|
baseUrl={baseUrl}
|
2019-02-07 19:58:20 +00:00
|
|
|
user={user}
|
2018-09-25 19:28:42 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderInvitedList = () => {
|
|
|
|
const { users } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<FlatList
|
|
|
|
data={users}
|
|
|
|
extraData={users}
|
|
|
|
keyExtractor={item => item._id}
|
|
|
|
style={[styles.list, sharedStyles.separatorVertical]}
|
|
|
|
renderItem={this.renderItem}
|
|
|
|
ItemSeparatorComponent={this.renderSeparator}
|
|
|
|
enableEmptySections
|
|
|
|
keyboardShouldPersistTaps='always'
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
2018-08-31 18:13:30 +00:00
|
|
|
|
2017-08-10 16:25:50 +00:00
|
|
|
render() {
|
2018-09-25 19:28:42 +00:00
|
|
|
const { channelName } = this.state;
|
2018-10-23 21:39:48 +00:00
|
|
|
const { users, isFetching } = this.props;
|
2018-09-25 19:28:42 +00:00
|
|
|
const userCount = users.length;
|
|
|
|
|
2017-08-10 16:25:50 +00:00
|
|
|
return (
|
2017-11-07 16:28:02 +00:00
|
|
|
<KeyboardView
|
2018-08-31 18:13:30 +00:00
|
|
|
contentContainerStyle={[sharedStyles.container, styles.container]}
|
2018-04-24 19:34:03 +00:00
|
|
|
keyboardVerticalOffset={128}
|
2017-11-07 16:28:02 +00:00
|
|
|
>
|
2019-03-12 16:23:06 +00:00
|
|
|
<StatusBar />
|
2018-10-23 21:39:48 +00:00
|
|
|
<SafeAreaView testID='create-channel-view' style={styles.container} forceInset={{ bottom: 'never' }}>
|
2018-08-31 18:13:30 +00:00
|
|
|
<ScrollView {...scrollPersistTaps}>
|
|
|
|
<View style={sharedStyles.separatorVertical}>
|
|
|
|
<TextInput
|
|
|
|
ref={ref => this.channelNameRef = ref}
|
|
|
|
style={styles.input}
|
|
|
|
label={I18n.t('Channel_Name')}
|
2018-09-25 19:28:42 +00:00
|
|
|
value={channelName}
|
2018-08-31 18:13:30 +00:00
|
|
|
onChangeText={this.onChangeText}
|
|
|
|
placeholder={I18n.t('Channel_Name')}
|
2019-03-29 19:36:07 +00:00
|
|
|
placeholderTextColor={COLOR_TEXT_DESCRIPTION}
|
2018-08-31 18:13:30 +00:00
|
|
|
returnKeyType='done'
|
|
|
|
testID='create-channel-name'
|
|
|
|
autoCorrect={false}
|
|
|
|
autoCapitalize='none'
|
|
|
|
underlineColorAndroid='transparent'
|
2018-05-24 20:17:45 +00:00
|
|
|
/>
|
2018-08-31 18:13:30 +00:00
|
|
|
{this.renderFormSeparator()}
|
|
|
|
{this.renderType()}
|
|
|
|
{this.renderFormSeparator()}
|
|
|
|
{this.renderReadOnly()}
|
|
|
|
{this.renderFormSeparator()}
|
|
|
|
{this.renderBroadcast()}
|
|
|
|
</View>
|
|
|
|
<View style={styles.invitedHeader}>
|
|
|
|
<Text style={styles.invitedTitle}>{I18n.t('Invite')}</Text>
|
|
|
|
<Text style={styles.invitedCount}>{userCount === 1 ? I18n.t('1_user') : I18n.t('N_users', { n: userCount })}</Text>
|
2018-05-24 20:17:45 +00:00
|
|
|
</View>
|
2018-08-31 18:13:30 +00:00
|
|
|
{this.renderInvitedList()}
|
2018-10-23 21:39:48 +00:00
|
|
|
<Loading visible={isFetching} />
|
2018-08-31 18:13:30 +00:00
|
|
|
</ScrollView>
|
|
|
|
</SafeAreaView>
|
2017-09-01 19:42:50 +00:00
|
|
|
</KeyboardView>
|
2017-08-10 16:25:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|