From 442d6b0bc584a8efdfb30fdbb8f89dfff5db4d2b Mon Sep 17 00:00:00 2001 From: enahum Date: Wed, 29 Mar 2017 00:04:44 -0300 Subject: [PATCH] Search (#415) * Align post_textbox paper plane * add search to manage | view | add channel members --- app/components/post_textbox/post_textbox.js | 3 +- .../channel_add_members/add_member_button.js | 47 ----- .../channel_add_members.js | 189 +++++++++++++++--- .../channel_add_members_container.js | 9 +- app/scenes/channel_members/channel_members.js | 167 ++++++++++++++-- .../channel_members_container.js | 7 +- .../channel_members/remove_member_button.js | 42 ++-- app/scenes/create_channel/create_channel.js | 120 +++++------ 8 files changed, 396 insertions(+), 188 deletions(-) delete mode 100644 app/scenes/channel_add_members/add_member_button.js diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js index a692ed378..f4bd0a2c0 100644 --- a/app/components/post_textbox/post_textbox.js +++ b/app/components/post_textbox/post_textbox.js @@ -368,7 +368,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { } }), alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', + paddingLeft: 2 }, typing: { paddingLeft: 10, diff --git a/app/scenes/channel_add_members/add_member_button.js b/app/scenes/channel_add_members/add_member_button.js deleted file mode 100644 index 6d6cf98de..000000000 --- a/app/scenes/channel_add_members/add_member_button.js +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React, {PropTypes} from 'react'; -import {connect} from 'react-redux'; -import { - TouchableOpacity, - View -} from 'react-native'; - -import FormattedText from 'app/components/formatted_text'; - -import {getTheme} from 'app/selectors/preferences'; - -function AddMemberButton(props) { - return ( - - props.emitter('add_members')} - style={{paddingHorizontal: 15}} - > - - - - ); -} - -AddMemberButton.propTypes = { - emitter: PropTypes.func.isRequired, - theme: PropTypes.object -}; - -AddMemberButton.defaultProps = { - theme: {} -}; - -function mapStateToProps(state) { - return { - theme: getTheme(state) - }; -} - -export default connect(mapStateToProps)(AddMemberButton); diff --git a/app/scenes/channel_add_members/channel_add_members.js b/app/scenes/channel_add_members/channel_add_members.js index a4630ea29..f9f16e3ad 100644 --- a/app/scenes/channel_add_members/channel_add_members.js +++ b/app/scenes/channel_add_members/channel_add_members.js @@ -2,30 +2,28 @@ // See License.txt for license information. import React, {PropTypes, PureComponent} from 'react'; +import {injectIntl, intlShape} from 'react-intl'; import { Alert, + Keyboard, + Platform, InteractionManager, StyleSheet, View } from 'react-native'; +import ActionButton from 'app/components/action_button'; import MemberList from 'app/components/custom_list'; +import SearchBar from 'app/components/search_bar'; import {createMembersSections, loadingText, renderMemberRow} from 'app/utils/member_list'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import AddMemberButton from './add_member_button'; -import {makeStyleSheetFromTheme} from 'app/utils/theme'; +import {Constants, RequestStatus} from 'mattermost-redux/constants'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; -const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { - return StyleSheet.create({ - container: { - flex: 1, - backgroundColor: theme.centerChannelBg - } - }); -}); - -export default class ChannelAddMembers extends PureComponent { +class ChannelAddMembers extends PureComponent { static propTypes = { + intl: intlShape.isRequired, theme: PropTypes.object.isRequired, currentChannel: PropTypes.object, currentChannelMemberCount: PropTypes.number, @@ -34,23 +32,37 @@ export default class ChannelAddMembers extends PureComponent { currentTeamMemberCount: PropTypes.number, preferences: PropTypes.object, loadMoreRequestStatus: PropTypes.string, + searchRequestStatus: PropTypes.string, + addChannelMemberStatus: PropTypes.string, subscribeToHeaderEvent: React.PropTypes.func, unsubscribeFromHeaderEvent: React.PropTypes.func, actions: PropTypes.shape({ getTeamStats: PropTypes.func.isRequired, getProfilesNotInChannel: PropTypes.func.isRequired, goBack: PropTypes.func.isRequired, - handleAddChannelMembers: PropTypes.func.isRequired + handleAddChannelMembers: PropTypes.func.isRequired, + searchProfiles: PropTypes.func.isRequired }) }; static navigationProps = { renderRightComponent: (props, emitter) => { - return ; + return ( + + ); } }; state = { + profiles: [], selectedMembers: {} }; @@ -59,14 +71,59 @@ export default class ChannelAddMembers extends PureComponent { } componentDidMount() { + if (Platform.OS === 'android') { + Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard); + } + InteractionManager.runAfterInteractions(() => { this.props.actions.getProfilesNotInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0); this.props.actions.getTeamStats(this.props.currentTeam.id); }); + + this.emitCanAddMembers(false); } componentWillUnmount() { this.props.unsubscribeFromHeaderEvent('add_members'); + + if (Platform.OS === 'android') { + Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard); + } + } + + componentWillReceiveProps(nextProps) { + const {loadMoreRequestStatus} = this.props; + if (loadMoreRequestStatus === RequestStatus.STARTED && + nextProps.loadMoreRequestStatus === RequestStatus.SUCCESS) { + this.setState({profiles: nextProps.membersNotInChannel}); + } else if (this.state.searching && + nextProps.searchRequestStatus === RequestStatus.SUCCESS) { + const results = nextProps.membersNotInChannel.filter((p) => { + const {term} = this.state; + return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) || + p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term); + }); + this.setState({profiles: results}); + } + + const {addChannelMemberStatus} = nextProps; + + if (this.props.addChannelMemberStatus !== addChannelMemberStatus) { + switch (addChannelMemberStatus) { + case RequestStatus.STARTED: + this.emitAdding(true); + this.setState({error: null}); + break; + case RequestStatus.SUCCESS: + this.emitAdding(false); + this.setState({error: null}); + this.props.actions.goBack(); + break; + case RequestStatus.FAILURE: + this.emitAdding(false); + break; + } + } } handleAddMembersPress = () => { @@ -79,36 +136,107 @@ export default class ChannelAddMembers extends PureComponent { return; } - actions.handleAddChannelMembers(currentTeam.id, currentChannel.id, membersToAdd).then(() => { - actions.goBack(); - }); - } + actions.handleAddChannelMembers(currentTeam.id, currentChannel.id, membersToAdd); + }; + + handleAndroidKeyboard = () => { + this.onSearchButtonPress(); + }; loadMoreMembers = () => { const {currentChannelMemberCount, currentTeamMemberCount, membersNotInChannel, loadMoreRequestStatus} = this.props; - if (loadMoreRequestStatus !== 'started' && membersNotInChannel.length < (currentTeamMemberCount - currentChannelMemberCount - 1)) { + if (loadMoreRequestStatus !== RequestStatus.STARTED && membersNotInChannel.length < (currentTeamMemberCount - currentChannelMemberCount - 1)) { this.props.actions.getProfilesNotInChannel(this.props.currentTeam.id, this.props.currentChannel.id, this.props.membersNotInChannel.length); } }; handleRowSelect = (id) => { const selectedMembers = Object.assign({}, this.state.selectedMembers, {[id]: !this.state.selectedMembers[id]}); + if (Object.keys(selectedMembers).length) { + this.emitCanAddMembers(true); + } else { + this.emitCanAddMembers(false); + } this.setState({ selectedMembers }); }; + emitCanAddMembers = (enabled) => { + EventEmitter.emit('can_add_members', enabled); + }; + + emitAdding = (loading) => { + EventEmitter.emit('adding_members', loading); + }; + + searchBarRef = (ref) => { + this.searchBar = ref; + }; + + onSearchButtonPress = () => { + this.searchBar.blur(); + }; + + searchProfiles = (event) => { + const term = event.nativeEvent.text.toLowerCase(); + + if (term) { + this.setState({searching: true, term}); + clearTimeout(this.searchTimeoutId); + + this.searchTimeoutId = setTimeout(() => { + this.props.actions.searchProfiles(term, {not_in_channel_id: this.props.currentChannel.id}); + }, Constants.SEARCH_TIMEOUT_MILLISECONDS); + } else { + this.cancelSearch(); + } + }; + + cancelSearch = () => { + this.setState({ + searching: false, + term: null, + page: 0, + profiles: this.props.membersNotInChannel + }); + }; + render() { - const style = getStyleFromTheme(this.props.theme); + const {intl, loadMoreRequestStatus, searchRequestStatus, preferences, theme} = this.props; + const {profiles, searching} = this.state; + const {formatMessage} = intl; + const isLoading = (loadMoreRequestStatus === RequestStatus.STARTED) || + (searchRequestStatus === RequestStatus.STARTED); + const style = getStyleFromTheme(theme); + const more = searching ? () => true : this.loadMoreMembers; return ( + + + { + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: theme.centerChannelBg + } + }); +}); + +export default injectIntl(ChannelAddMembers); diff --git a/app/scenes/channel_add_members/channel_add_members_container.js b/app/scenes/channel_add_members/channel_add_members_container.js index 2726cd5de..31dd21638 100644 --- a/app/scenes/channel_add_members/channel_add_members_container.js +++ b/app/scenes/channel_add_members/channel_add_members_container.js @@ -13,7 +13,7 @@ import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences' import {getCurrentTeam, getCurrentTeamStats} from 'mattermost-redux/selectors/entities/teams'; import {getProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users'; import {getTeamStats} from 'mattermost-redux/actions/teams'; -import {getProfilesNotInChannel} from 'mattermost-redux/actions/users'; +import {getProfilesNotInChannel, searchProfiles} from 'mattermost-redux/actions/users'; import ChannelAddMembers from './channel_add_members'; @@ -30,7 +30,9 @@ function mapStateToProps(state) { currentChannelMemberCount, preferences: getMyPreferences(state), loadMoreRequestStatus: state.requests.users.getProfilesNotInChannel.status, - addChannelMemberRequestStatus: state.requests.channels.addChannelMember + addChannelMemberRequestStatus: state.requests.channels.addChannelMember, + searchRequestStatus: state.requests.users.searchProfiles.status, + addChannelMemberStatus: state.requests.channels.addChannelMember.status }; } @@ -40,7 +42,8 @@ function mapDispatchToProps(dispatch) { getTeamStats, getProfilesNotInChannel, goBack, - handleAddChannelMembers + handleAddChannelMembers, + searchProfiles }, dispatch) }; } diff --git a/app/scenes/channel_members/channel_members.js b/app/scenes/channel_members/channel_members.js index 1080d965d..b6652e7e1 100644 --- a/app/scenes/channel_members/channel_members.js +++ b/app/scenes/channel_members/channel_members.js @@ -4,6 +4,8 @@ import React, {PropTypes, PureComponent} from 'react'; import { Alert, + Keyboard, + Platform, InteractionManager, StyleSheet, View @@ -11,23 +13,18 @@ import { import {injectIntl, intlShape} from 'react-intl'; import MemberList from 'app/components/custom_list'; +import SearchBar from 'app/components/search_bar'; import {createMembersSections, loadingText} from 'app/utils/member_list'; import MemberListRow from 'app/components/custom_list/member_list_row'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; -import {makeStyleSheetFromTheme} from 'app/utils/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; + +import {Constants, RequestStatus} from 'mattermost-redux/constants'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; import ChannelMembersTitle from './channel_members_title'; import RemoveMemberButton from './remove_member_button'; -const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { - return StyleSheet.create({ - container: { - flex: 1, - backgroundColor: theme.centerChannelBg - } - }); -}); - class ChannelMembers extends PureComponent { static propTypes = { intl: intlShape.isRequired, @@ -39,13 +36,16 @@ class ChannelMembers extends PureComponent { currentTeam: PropTypes.object, preferences: PropTypes.object, requestStatus: PropTypes.string, + searchRequestStatus: PropTypes.string, + removeMembersStatus: PropTypes.string, canManageUsers: PropTypes.bool.isRequired, subscribeToHeaderEvent: React.PropTypes.func, unsubscribeFromHeaderEvent: React.PropTypes.func, actions: PropTypes.shape({ getProfilesInChannel: PropTypes.func.isRequired, goBack: PropTypes.func.isRequired, - handleRemoveChannelMembers: PropTypes.func.isRequired + handleRemoveChannelMembers: PropTypes.func.isRequired, + searchProfiles: PropTypes.func.isRequired }) }; @@ -59,6 +59,7 @@ class ChannelMembers extends PureComponent { }; state = { + profiles: [], selectedMembers: {} }; @@ -67,13 +68,58 @@ class ChannelMembers extends PureComponent { } componentDidMount() { + if (Platform.OS === 'android') { + Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard); + } + InteractionManager.runAfterInteractions(() => { this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0); }); + + this.emitCanRemoveMembers(false); } componentWillUnmount() { this.props.unsubscribeFromHeaderEvent('remove_members'); + + if (Platform.OS === 'android') { + Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard); + } + } + + componentWillReceiveProps(nextProps) { + const {requestStatus} = this.props; + if (requestStatus === RequestStatus.STARTED && + nextProps.requestStatus === RequestStatus.SUCCESS) { + this.setState({profiles: nextProps.currentChannelMembers}); + } else if (this.state.searching && + nextProps.searchRequestStatus === RequestStatus.SUCCESS) { + const results = nextProps.currentChannelMembers.filter((p) => { + const {term} = this.state; + return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) || + p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term); + }); + this.setState({profiles: results}); + } + + const {removeMembersStatus} = nextProps; + + if (this.props.removeMembersStatus !== removeMembersStatus) { + switch (removeMembersStatus) { + case RequestStatus.STARTED: + this.emitRemoving(true); + this.setState({error: null}); + break; + case RequestStatus.SUCCESS: + this.emitRemoving(false); + this.setState({error: null}); + this.props.actions.goBack(); + break; + case RequestStatus.FAILURE: + this.emitRemoving(false); + break; + } + } } handleRemoveMembersPress = () => { @@ -113,11 +159,13 @@ class ChannelMembers extends PureComponent { ); }; + handleAndroidKeyboard = () => { + this.onSearchButtonPress(); + }; + removeMembers = (membersToRemove) => { const {actions, currentTeam, currentChannel} = this.props; - actions.handleRemoveChannelMembers(currentTeam.id, currentChannel.id, membersToRemove).then(() => { - actions.goBack(); - }); + actions.handleRemoveChannelMembers(currentTeam.id, currentChannel.id, membersToRemove); }; loadMoreMembers = () => { @@ -128,11 +176,56 @@ class ChannelMembers extends PureComponent { handleRowSelect = (id) => { const selectedMembers = Object.assign({}, this.state.selectedMembers, {[id]: !this.state.selectedMembers[id]}); + if (Object.keys(selectedMembers).length) { + this.emitCanRemoveMembers(true); + } else { + this.emitCanRemoveMembers(false); + } this.setState({ selectedMembers }); }; + emitCanRemoveMembers = (enabled) => { + EventEmitter.emit('can_remove_members', enabled); + }; + + emitRemoving = (loading) => { + EventEmitter.emit('removing_members', loading); + }; + + searchBarRef = (ref) => { + this.searchBar = ref; + }; + + onSearchButtonPress = () => { + this.searchBar.blur(); + }; + + searchProfiles = (event) => { + const term = event.nativeEvent.text.toLowerCase(); + + if (term) { + this.setState({searching: true, term}); + clearTimeout(this.searchTimeoutId); + + this.searchTimeoutId = setTimeout(() => { + this.props.actions.searchProfiles(term, {in_channel_id: this.props.currentChannel.id}); + }, Constants.SEARCH_TIMEOUT_MILLISECONDS); + } else { + this.cancelSearch(); + } + }; + + cancelSearch = () => { + this.setState({ + searching: false, + term: null, + page: 0, + profiles: this.props.currentChannelMembers + }); + }; + renderMemberRow = (user, sectionId, rowId, preferences, theme, selectable, onPress, onSelect) => { const {id, username} = user; const displayName = displayUsername(user, preferences); @@ -157,20 +250,43 @@ class ChannelMembers extends PureComponent { disableSelect={disableSelect} /> ); - } + }; render() { - const {canManageUsers, theme} = this.props; + const {canManageUsers, intl, preferences, requestStatus, searchRequestStatus, theme} = this.props; + const {formatMessage} = intl; + const {profiles, searching} = this.state; + const isLoading = (requestStatus === RequestStatus.STARTED) || + (searchRequestStatus === RequestStatus.STARTED); + const more = searching ? () => true : this.loadMoreMembers; const style = getStyleFromTheme(theme); return ( + + + { + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: theme.centerChannelBg + } + }); +}); + export default injectIntl(ChannelMembers); diff --git a/app/scenes/channel_members/channel_members_container.js b/app/scenes/channel_members/channel_members_container.js index 075fdf819..848d52d3d 100644 --- a/app/scenes/channel_members/channel_members_container.js +++ b/app/scenes/channel_members/channel_members_container.js @@ -12,7 +12,7 @@ import {getCurrentChannel, getCurrentChannelStats, canManageChannelMembers} from import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; import {getProfilesInCurrentChannel} from 'mattermost-redux/selectors/entities/users'; -import {getProfilesInChannel} from 'mattermost-redux/actions/users'; +import {getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users'; import ChannelMembers from './channel_members'; @@ -28,6 +28,8 @@ function mapStateToProps(state) { currentTeam: getCurrentTeam(state), preferences: getMyPreferences(state), requestStatus: state.requests.users.getProfilesInChannel.status, + searchRequestStatus: state.requests.users.searchProfiles.status, + removeMembersStatus: state.requests.channels.removeChannelMember.status, canManageUsers: canManageChannelMembers(state) }; } @@ -37,7 +39,8 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ getProfilesInChannel, goBack, - handleRemoveChannelMembers + handleRemoveChannelMembers, + searchProfiles }, dispatch) }; } diff --git a/app/scenes/channel_members/remove_member_button.js b/app/scenes/channel_members/remove_member_button.js index 399fd6cf1..8d0262718 100644 --- a/app/scenes/channel_members/remove_member_button.js +++ b/app/scenes/channel_members/remove_member_button.js @@ -3,15 +3,10 @@ import React, {PropTypes} from 'react'; import {connect} from 'react-redux'; -import { - TouchableOpacity, - View -} from 'react-native'; -import FormattedText from 'app/components/formatted_text'; +import ActionButton from 'app/components/action_button'; -import {getCurrentChannel, canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels'; -import {getTheme} from 'app/selectors/preferences'; +import {canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels'; function RemoveMemberButton(props) { const {canManageUsers} = props; @@ -20,37 +15,26 @@ function RemoveMemberButton(props) { } return ( - - props.emitter('remove_members')} - style={{paddingHorizontal: 15}} - > - - - + ); } RemoveMemberButton.propTypes = { emitter: PropTypes.func.isRequired, - canManageUsers: PropTypes.bool.isRequired, - currentChannel: PropTypes.object.isRequired, - theme: PropTypes.object -}; - -RemoveMemberButton.defaultProps = { - theme: {} + canManageUsers: PropTypes.bool.isRequired }; function mapStateToProps(state) { return { - canManageUsers: canManageChannelMembers(state), - currentChannel: getCurrentChannel(state), - theme: getTheme(state) + canManageUsers: canManageChannelMembers(state) }; } diff --git a/app/scenes/create_channel/create_channel.js b/app/scenes/create_channel/create_channel.js index c940154e1..a65566497 100644 --- a/app/scenes/create_channel/create_channel.js +++ b/app/scenes/create_channel/create_channel.js @@ -25,66 +25,6 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter'; import ActionButton from 'app/components/action_button'; -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - return StyleSheet.create({ - container: { - flex: 1, - backgroundColor: theme.centerChannelBg - }, - scrollView: { - flex: 1, - backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), - paddingTop: 30 - }, - errorContainer: { - position: 'absolute' - }, - errorWrapper: { - justifyContent: 'center', - alignItems: 'center', - marginBottom: 10 - }, - inputContainer: { - marginTop: 10, - borderTopWidth: 1, - borderBottomWidth: 1, - borderTopColor: changeOpacity(theme.centerChannelColor, 0.1), - borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1), - backgroundColor: theme.centerChannelBg - }, - input: { - color: theme.centerChannelColor, - fontSize: 14, - height: 40, - paddingHorizontal: 15 - }, - titleContainer30: { - flexDirection: 'row', - marginTop: 30 - }, - titleContainer15: { - flexDirection: 'row', - marginTop: 15 - }, - title: { - fontSize: 14, - color: theme.centerChannelColor, - marginLeft: 15 - }, - optional: { - color: changeOpacity(theme.centerChannelColor, 0.5), - fontSize: 14, - marginLeft: 5 - }, - helpText: { - fontSize: 14, - color: changeOpacity(theme.centerChannelColor, 0.5), - marginTop: 10, - marginHorizontal: 15 - } - }); -}); - class CreateChannel extends PureComponent { static propTypes = { intl: intlShape.isRequired, @@ -372,4 +312,64 @@ class CreateChannel extends PureComponent { } } +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: theme.centerChannelBg + }, + scrollView: { + flex: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), + paddingTop: 30 + }, + errorContainer: { + position: 'absolute' + }, + errorWrapper: { + justifyContent: 'center', + alignItems: 'center', + marginBottom: 10 + }, + inputContainer: { + marginTop: 10, + borderTopWidth: 1, + borderBottomWidth: 1, + borderTopColor: changeOpacity(theme.centerChannelColor, 0.1), + borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1), + backgroundColor: theme.centerChannelBg + }, + input: { + color: theme.centerChannelColor, + fontSize: 14, + height: 40, + paddingHorizontal: 15 + }, + titleContainer30: { + flexDirection: 'row', + marginTop: 30 + }, + titleContainer15: { + flexDirection: 'row', + marginTop: 15 + }, + title: { + fontSize: 14, + color: theme.centerChannelColor, + marginLeft: 15 + }, + optional: { + color: changeOpacity(theme.centerChannelColor, 0.5), + fontSize: 14, + marginLeft: 5 + }, + helpText: { + fontSize: 14, + color: changeOpacity(theme.centerChannelColor, 0.5), + marginTop: 10, + marginHorizontal: 15 + } + }); +}); + export default injectIntl(CreateChannel);