import React from 'react'; import PropTypes from 'prop-types'; import { Text, View, ScrollView, TouchableOpacity, Keyboard, Alert } from 'react-native'; import { connect } from 'react-redux'; import SafeAreaView from 'react-native-safe-area-view'; import equal from 'deep-equal'; import database from '../../lib/database'; import { eraseRoom as eraseRoomAction } from '../../actions/room'; import KeyboardView from '../../presentation/KeyboardView'; import sharedStyles from '../Styles'; import styles from './styles'; import scrollPersistTaps from '../../utils/scrollPersistTaps'; import { showErrorAlert } from '../../utils/info'; import { LISTENER } from '../../containers/Toast'; import EventEmitter from '../../utils/events'; import RocketChat from '../../lib/rocketchat'; import RCTextInput from '../../containers/TextInput'; import Loading from '../../containers/Loading'; import SwitchContainer from './SwitchContainer'; import random from '../../utils/random'; import log from '../../utils/log'; import I18n from '../../i18n'; import StatusBar from '../../containers/StatusBar'; const PERMISSION_SET_READONLY = 'set-readonly'; const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly'; const PERMISSION_ARCHIVE = 'archive-room'; const PERMISSION_UNARCHIVE = 'unarchive-room'; const PERMISSION_DELETE_C = 'delete-c'; const PERMISSION_DELETE_P = 'delete-p'; const PERMISSIONS_ARRAY = [ PERMISSION_SET_READONLY, PERMISSION_SET_REACT_WHEN_READONLY, PERMISSION_ARCHIVE, PERMISSION_UNARCHIVE, PERMISSION_DELETE_C, PERMISSION_DELETE_P ]; class RoomInfoEditView extends React.Component { static navigationOptions = { title: I18n.t('Room_Info_Edit') } static propTypes = { navigation: PropTypes.object, eraseRoom: PropTypes.func }; constructor(props) { super(props); this.state = { room: {}, permissions: {}, name: '', description: '', topic: '', announcement: '', joinCode: '', nameError: {}, saving: false, t: false, ro: false, reactWhenReadOnly: false, archived: false }; this.loadRoom(); } shouldComponentUpdate(nextProps, nextState) { if (!equal(nextState, this.state)) { return true; } if (!equal(nextProps, this.props)) { return true; } return false; } componentWillUnmount() { if (this.querySubscription && this.querySubscription.unsubscribe) { this.querySubscription.unsubscribe(); } } // eslint-disable-next-line react/sort-comp loadRoom = async() => { const { navigation } = this.props; const rid = navigation.getParam('rid', null); if (!rid) { return; } try { const db = database.active; const sub = await db.collections.get('subscriptions').find(rid); const observable = sub.observe(); this.querySubscription = observable.subscribe((data) => { this.room = data; this.init(this.room); }); const permissions = await RocketChat.hasPermission(PERMISSIONS_ARRAY, rid); this.setState({ permissions }); } catch (e) { log(e); } } init = (room) => { const { name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired } = room; // fake password just to user knows about it this.randomValue = random(15); this.setState({ room, name, description, topic, announcement, t: t === 'p', ro, reactWhenReadOnly, joinCode: joinCodeRequired ? this.randomValue : '', archived: room.archived }); } clearErrors = () => { this.setState({ nameError: {} }); } reset = () => { this.clearErrors(); this.init(this.room); } formIsChanged = () => { const { room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode } = this.state; return !(room.name === name && room.description === description && room.topic === topic && room.announcement === announcement && this.randomValue === joinCode && room.t === 'p' === t && room.ro === ro && room.reactWhenReadOnly === reactWhenReadOnly ); } submit = async() => { Keyboard.dismiss(); const { room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode } = this.state; this.setState({ saving: true }); let error = false; if (!this.formIsChanged()) { showErrorAlert(I18n.t('Nothing_to_save')); return; } // Clear error objects await this.clearErrors(); const params = {}; // Name if (room.name !== name) { params.roomName = name; } // Description if (room.description !== description) { params.roomDescription = description; } // Topic if (room.topic !== topic) { params.roomTopic = topic; } // Announcement if (room.announcement !== announcement) { params.roomAnnouncement = announcement; } // Room Type if (room.t !== t) { params.roomType = t ? 'p' : 'c'; } // Read Only if (room.ro !== ro) { params.readOnly = ro; } // React When Read Only if (room.reactWhenReadOnly !== reactWhenReadOnly) { params.reactWhenReadOnly = reactWhenReadOnly; } // Join Code if (this.randomValue !== joinCode) { params.joinCode = joinCode; } try { await RocketChat.saveRoomSettings(room.rid, params); } catch (e) { if (e.error === 'error-invalid-room-name') { this.setState({ nameError: e }); } error = true; log(e); } await this.setState({ saving: false }); setTimeout(() => { if (error) { showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('saving_settings') })); } else { EventEmitter.emit(LISTENER, { message: I18n.t('Settings_succesfully_changed') }); } }, 100); } delete = () => { const { room } = this.state; const { eraseRoom } = this.props; Alert.alert( I18n.t('Are_you_sure_question_mark'), I18n.t('Delete_Room_Warning'), [ { text: I18n.t('Cancel'), style: 'cancel' }, { text: I18n.t('Yes_action_it', { action: I18n.t('delete') }), style: 'destructive', onPress: () => eraseRoom(room.rid, room.t) } ], { cancelable: false } ); } toggleArchive = () => { const { room } = this.state; const { rid, archived, t } = room; const action = I18n.t(`${ archived ? 'un' : '' }archive`); Alert.alert( I18n.t('Are_you_sure_question_mark'), I18n.t('Do_you_really_want_to_key_this_room_question_mark', { key: action }), [ { text: I18n.t('Cancel'), style: 'cancel' }, { text: I18n.t('Yes_action_it', { action }), style: 'destructive', onPress: async() => { try { await RocketChat.toggleArchiveRoom(rid, t, !archived); } catch (e) { log(e); } } } ], { cancelable: false } ); } hasDeletePermission = () => { const { room, permissions } = this.state; return ( room.t === 'p' ? permissions[PERMISSION_DELETE_P] : permissions[PERMISSION_DELETE_C] ); } hasArchivePermission = () => { const { permissions } = this.state; return (permissions[PERMISSION_ARCHIVE] || permissions[PERMISSION_UNARCHIVE]); }; render() { const { name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived } = this.state; return ( { this.name = e; }} label={I18n.t('Name')} value={name} onChangeText={value => this.setState({ name: value })} onSubmitEditing={() => { this.description.focus(); }} error={nameError} testID='room-info-edit-view-name' /> { this.description = e; }} label={I18n.t('Description')} value={description} onChangeText={value => this.setState({ description: value })} onSubmitEditing={() => { this.topic.focus(); }} testID='room-info-edit-view-description' /> { this.topic = e; }} label={I18n.t('Topic')} value={topic} onChangeText={value => this.setState({ topic: value })} onSubmitEditing={() => { this.announcement.focus(); }} testID='room-info-edit-view-topic' /> { this.announcement = e; }} label={I18n.t('Announcement')} value={announcement} onChangeText={value => this.setState({ announcement: value })} onSubmitEditing={() => { this.joinCode.focus(); }} testID='room-info-edit-view-announcement' /> { this.joinCode = e; }} label={I18n.t('Password')} value={joinCode} onChangeText={value => this.setState({ joinCode: value })} onSubmitEditing={this.submit} secureTextEntry testID='room-info-edit-view-password' /> this.setState({ t: value })} testID='room-info-edit-view-t' /> this.setState({ ro: value })} disabled={!permissions[PERMISSION_SET_READONLY] || room.broadcast} testID='room-info-edit-view-ro' /> {ro && !room.broadcast ? ( this.setState({ reactWhenReadOnly: value })} disabled={!permissions[PERMISSION_SET_REACT_WHEN_READONLY]} testID='room-info-edit-view-react-when-ro' /> ) : null } {room.broadcast ? [ {I18n.t('Broadcast_Channel')}, ] : null } {I18n.t('SAVE')} {I18n.t('RESET')} { archived ? I18n.t('UNARCHIVE') : I18n.t('ARCHIVE') } {I18n.t('DELETE')} ); } } const mapDispatchToProps = dispatch => ({ eraseRoom: (rid, t) => dispatch(eraseRoomAction(rid, t)) }); export default connect(null, mapDispatchToProps)(RoomInfoEditView);