diff --git a/app/actions/navigation/index.js b/app/actions/navigation/index.js index fdd7e5d16..5ef702515 100644 --- a/app/actions/navigation/index.js +++ b/app/actions/navigation/index.js @@ -6,6 +6,22 @@ import Routes from 'app/navigation/routes'; import {Constants} from 'mattermost-redux/constants'; import {selectPost} from 'mattermost-redux/actions/posts'; +export function closeDrawers() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_CLOSE_DRAWERS + }, getState); + }; +} + +export function closeModal() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_CLOSE_MODAL + }, getState); + }; +} + export function goBack() { return async (dispatch, getState) => { dispatch({ @@ -14,10 +30,99 @@ export function goBack() { }; } -export function closeDrawers() { +export function goToAccountNotifications() { return async (dispatch, getState) => { dispatch({ - type: NavigationTypes.NAVIGATION_CLOSE_DRAWERS + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.AccountNotifications + }, getState); + }; +} + +export function goToAccountSettings() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.AccountSettings + }, getState); + }; +} + +export function goToChannelAddMembers() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.ChannelAddMembers + }, getState); + }; +} + +export function goToChannelInfo() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.ChannelInfo + }, getState); + }; +} + +export function goToChannelMembers() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.ChannelMembers + }, getState); + }; +} + +export function goToCreateChannel(channelType) { + return async (dispatch, getState) => { + closeDrawers()(dispatch, getState); + let type; + let route; + switch (channelType) { + case Constants.OPEN_CHANNEL: + type = NavigationTypes.NAVIGATION_PUSH; + route = Routes.CreatePublicChannel; + break; + case Constants.PRIVATE_CHANNEL: + type = NavigationTypes.NAVIGATION_MODAL; + route = Routes.CreatePrivateChannel; + break; + default: + return; + } + + dispatch({ + type, + route, + props: { + channelType + } + }, getState); + }; +} + +export function goToImagePreviewModal(post, fileId) { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_MODAL, + route: { + ...Routes.ImagePreview, + props: { + post, + fileId + } + } + }, getState); + }; +} + +export function goToLoadTeam() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_PUSH, + route: Routes.LoadTeam }, getState); }; } @@ -58,52 +163,15 @@ export function goToSaml() { }; } -export function goToLoadTeam() { +export function goToSelectTeam() { return async (dispatch, getState) => { dispatch({ type: NavigationTypes.NAVIGATION_PUSH, - route: Routes.LoadTeam - }, getState); - }; -} - -export function goToModalSelectTeam() { - return async (dispatch, getState) => { - closeDrawers()(dispatch, getState); - dispatch({ - type: NavigationTypes.NAVIGATION_MODAL, route: Routes.SelectTeam }, getState); }; } -export function goToChannelInfo() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_PUSH, - route: Routes.ChannelInfo - }, getState); - }; -} - -export function goToChannelMembers() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_PUSH, - route: Routes.ChannelMembers - }, getState); - }; -} - -export function goToChannelAddMembers() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_PUSH, - route: Routes.ChannelAddMembers - }, getState); - }; -} - export function goToThread(channelId, rootId) { return async (dispatch, getState) => { selectPost(rootId)(dispatch, getState); @@ -145,11 +213,19 @@ export function openChannelDrawer() { }; } -export function openRightMenuDrawer() { +export function openSettingsModal() { return async (dispatch, getState) => { dispatch({ - type: NavigationTypes.NAVIGATION_OPEN_RIGHT_DRAWER, - route: Routes.RightMenuDrawer + type: NavigationTypes.NAVIGATION_MODAL, + route: Routes.Settings + }, getState); + }; +} + +export function requestCloseModal() { + return async (dispatch, getState) => { + dispatch({ + type: NavigationTypes.NAVIGATION_REQUEST_CLOSE_MODAL }, getState); }; } @@ -185,81 +261,3 @@ export function showDirectMessagesModal() { }, getState); }; } - -export function closeModal() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_CLOSE_MODAL - }, getState); - }; -} - -export function goToModalAccountSettings() { - return async (dispatch, getState) => { - closeDrawers()(dispatch, getState); - dispatch({ - type: NavigationTypes.NAVIGATION_MODAL, - route: Routes.AccountSettings - }, getState); - }; -} - -export function goToCreateChannel(channelType) { - return async (dispatch, getState) => { - closeDrawers()(dispatch, getState); - let type; - let route; - switch (channelType) { - case Constants.OPEN_CHANNEL: - type = NavigationTypes.NAVIGATION_PUSH; - route = Routes.CreatePublicChannel; - break; - case Constants.PRIVATE_CHANNEL: - type = NavigationTypes.NAVIGATION_MODAL; - route = Routes.CreatePrivateChannel; - break; - default: - return; - } - - dispatch({ - type, - route, - props: { - channelType - } - }, getState); - }; -} - -export function goToAccountNotifications() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_PUSH, - route: Routes.AccountNotifications - }, getState); - }; -} - -export function requestCloseModal() { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_REQUEST_CLOSE_MODAL - }, getState); - }; -} - -export function goToImagePreviewModal(post, fileId) { - return async (dispatch, getState) => { - dispatch({ - type: NavigationTypes.NAVIGATION_MODAL, - route: { - ...Routes.ImagePreview, - props: { - post, - fileId - } - } - }, getState); - }; -} diff --git a/app/components/channel_drawer_list/channel_drawer_list.js b/app/components/channel_drawer_list/channel_drawer_list.js index 974e7c5bb..dcae7afb7 100644 --- a/app/components/channel_drawer_list/channel_drawer_list.js +++ b/app/components/channel_drawer_list/channel_drawer_list.js @@ -36,9 +36,9 @@ class ChannelDrawerList extends Component { actions: PropTypes.shape({ closeDMChannel: PropTypes.func.isRequired, goToCreateChannel: PropTypes.func.isRequired, - goToModalAccountSettings: React.PropTypes.func.isRequired, leaveChannel: PropTypes.func.isRequired, markFavorite: PropTypes.func.isRequired, + openSettingsModal: React.PropTypes.func.isRequired, unmarkFavorite: PropTypes.func.isRequired, showOptionsModal: PropTypes.func.isRequired, showDirectMessagesModal: PropTypes.func.isRequired, @@ -412,7 +412,7 @@ class ChannelDrawerList extends Component { const settings = ( this.props.actions.goToModalAccountSettings()} + onPress={() => this.props.actions.openSettingsModal()} > diff --git a/app/navigation/routes.js b/app/navigation/routes.js index 13be361db..699c9fa52 100644 --- a/app/navigation/routes.js +++ b/app/navigation/routes.js @@ -18,7 +18,7 @@ import { MoreChannels, MoreDirectMessages, OptionsModal, - RightMenuDrawer, + Settings, Root, Saml, Search, @@ -149,10 +149,6 @@ export const Routes = { key: 'OptionsModal', component: OptionsModal }, - RightMenuDrawer: { - key: 'RightMenuDrawer', - component: RightMenuDrawer - }, Root: { key: 'Root', component: Root @@ -180,10 +176,18 @@ export const Routes = { SelectTeam: { key: 'SelectTeam', component: SelectTeam, + transition: RouteTransitions.Horizontal, navigationProps: { title: {id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'} } }, + Settings: { + key: 'Settings', + component: Settings, + navigationProps: { + title: {id: 'mobile.routes.settings', defaultMessage: 'Settings'} + } + }, Thread: { key: 'Thread', transition: RouteTransitions.Horizontal, diff --git a/app/scenes/channel/channel.js b/app/scenes/channel/channel.js index 9d0708655..680e4765b 100644 --- a/app/scenes/channel/channel.js +++ b/app/scenes/channel/channel.js @@ -16,7 +16,6 @@ import {Constants} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; import ChannelDrawerButton from './channel_drawer_button'; -import ChannelMenuButton from './channel_menu_button'; import ChannelTitle from './channel_title'; import ChannelPostList from './channel_post_list'; @@ -28,7 +27,6 @@ export default class Channel extends React.PureComponent { selectFirstAvailableTeam: React.PropTypes.func.isRequired, selectInitialChannel: React.PropTypes.func.isRequired, openChannelDrawer: React.PropTypes.func.isRequired, - openRightMenuDrawer: React.PropTypes.func.isRequired, handlePostDraftChanged: React.PropTypes.func.isRequired, goToChannelInfo: React.PropTypes.func.isRequired, initWebSocket: React.PropTypes.func.isRequired, @@ -51,15 +49,11 @@ export default class Channel extends React.PureComponent { }, renderTitleComponent: (props, emitter) => { return ; - }, - renderRightComponent: (props, emitter) => { - return ; } }; componentWillMount() { this.props.subscribeToHeaderEvent('open_channel_drawer', this.openChannelDrawer); - this.props.subscribeToHeaderEvent('open_right_menu', this.openRightMenuDrawer); this.props.subscribeToHeaderEvent('show_channel_info', this.props.actions.goToChannelInfo); EventEmitter.on('leave_team', this.handleLeaveTeam); this.props.actions.initWebSocket(Platform.OS); @@ -99,11 +93,6 @@ export default class Channel extends React.PureComponent { this.props.actions.openChannelDrawer(); }; - openRightMenuDrawer = () => { - this.postTextbox.getWrappedInstance().blur(); - this.props.actions.openRightMenuDrawer(); - }; - attachPostTextbox = (ref) => { this.postTextbox = ref; }; diff --git a/app/scenes/channel/channel_container.js b/app/scenes/channel/channel_container.js index 8deac5c10..3842a9836 100644 --- a/app/scenes/channel/channel_container.js +++ b/app/scenes/channel/channel_container.js @@ -7,8 +7,7 @@ import navigationSceneConnect from '../navigationSceneConnect'; import { goToChannelInfo, - openChannelDrawer, - openRightMenuDrawer + openChannelDrawer } from 'app/actions/navigation'; import { loadChannelsIfNecessary, @@ -48,7 +47,6 @@ function mapDispatchToProps(dispatch) { selectFirstAvailableTeam, selectInitialChannel, openChannelDrawer, - openRightMenuDrawer, handlePostDraftChanged, goToChannelInfo, initWebSocket, diff --git a/app/scenes/channel/channel_menu_button.js b/app/scenes/channel/channel_menu_button.js deleted file mode 100644 index 673bb4767..000000000 --- a/app/scenes/channel/channel_menu_button.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React, {PropTypes, PureComponent} from 'react'; -import {connect} from 'react-redux'; -import { - TouchableOpacity, - View -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome'; - -import {getTheme} from 'app/selectors/preferences'; - -import EventEmitter from 'mattermost-redux/utils/event_emitter'; - -class ChannelMenuButton extends PureComponent { - static propTypes = { - emitter: PropTypes.func.isRequired, - theme: PropTypes.object - }; - - static defaultProps = { - currentChannel: {}, - theme: {} - }; - - constructor(props) { - super(props); - - this.state = { - opacity: 1 - }; - } - - componentDidMount() { - EventEmitter.on('drawer_opacity', this.setOpacity); - } - - componentWillUnmount() { - EventEmitter.off('drawer_opacity', this.setOpacity); - } - - setOpacity = (value) => { - this.setState({opacity: value > 0 ? 0.1 : 1}); - }; - - render() { - return ( - - this.props.emitter('open_right_menu')}> - - - - ); - } -} - -function mapStateToProps(state) { - return { - theme: getTheme(state) - }; -} - -export default connect(mapStateToProps)(ChannelMenuButton); diff --git a/app/scenes/index.js b/app/scenes/index.js index 6675e4d68..b94177ecf 100644 --- a/app/scenes/index.js +++ b/app/scenes/index.js @@ -17,7 +17,7 @@ import Mfa from './mfa'; import MoreChannels from './more_channels'; import MoreDirectMessages from './more_dms'; import OptionsModal from './options_modal'; -import RightMenuDrawer from './right_menu_drawer'; +import Settings from './settings'; import Root from './root/root_container.js'; import Search from './search/search_container.js'; import SelectServer from './select_server/select_server_container.js'; @@ -43,7 +43,7 @@ module.exports = { MoreChannels, MoreDirectMessages, OptionsModal, - RightMenuDrawer, + Settings, Root, Search, SelectServer, diff --git a/app/scenes/right_menu_drawer/index.js b/app/scenes/right_menu_drawer/index.js deleted file mode 100644 index ef150b1e5..000000000 --- a/app/scenes/right_menu_drawer/index.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import RightMenuDrawerContainer from './right_menu_drawer_container'; - -export default RightMenuDrawerContainer; diff --git a/app/scenes/right_menu_drawer/right_menu_drawer.js b/app/scenes/right_menu_drawer/right_menu_drawer.js deleted file mode 100644 index dfb59d7e2..000000000 --- a/app/scenes/right_menu_drawer/right_menu_drawer.js +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React from 'react'; -import { - ScrollView, - StyleSheet, - Platform, - View, - Linking -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome'; - -import FormattedText from 'app/components/formatted_text'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; - -import RightMenuDrawerItem from './right_menu_drawer_item'; - -export default class RightMenuDrawer extends React.Component { - static propTypes = { - errors: React.PropTypes.array.isRequired, - currentUserId: React.PropTypes.string.isRequired, - currentTeamId: React.PropTypes.string.isRequired, - actions: React.PropTypes.shape({ - goToModalSelectTeam: React.PropTypes.func.isRequired, - clearErrors: React.PropTypes.func.isRequired, - logout: React.PropTypes.func.isRequired - }).isRequired, - theme: React.PropTypes.object - }; - - openErrorEmail = () => { - const recipient = 'feedback@mattermost.com'; - const subject = 'Problem with Mattermost React Native app'; - Linking.openURL( - `mailto:${recipient}?subject=${subject}&body=${this.errorEmailBody()}` - ); - this.props.actions.clearErrors(); - }; - - errorEmailBody = () => { - const {currentUserId, currentTeamId, errors} = this.props; - let contents = [ - `Current User Id: ${currentUserId}`, - `Current Team Id: ${currentTeamId}` - ]; - if (errors.length) { - contents = contents.concat([ - '', - 'Errors:', - JSON.stringify(errors.map((e) => e.error)) - ]); - } - return contents.join('\n'); - }; - - render() { - const Styles = getStyleSheet(this.props.theme); - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } -} - -function Divider(props) { - return ; -} - -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - return StyleSheet.create({ - container: { - backgroundColor: theme.sidebarBg, - flex: 1, - ...Platform.select({ - ios: { - paddingTop: 20 - }, - android: { - paddingTop: 5 - } - }) - }, - itemText: { - color: theme.sidebarText - }, - icon: { - color: theme.sidebarText, - width: 25 - }, - mentionIcon: { - fontSize: 17, - fontWeight: 'bold' - }, - divider: { - borderColor: changeOpacity(theme.sidebarText, 0.2), - borderTopWidth: StyleSheet.hairlineWidth - } - }); -}); diff --git a/app/scenes/right_menu_drawer/right_menu_drawer_item.js b/app/scenes/right_menu_drawer/right_menu_drawer_item.js deleted file mode 100644 index fddba3433..000000000 --- a/app/scenes/right_menu_drawer/right_menu_drawer_item.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React, {Component, PropTypes} from 'react'; -import {StyleSheet, TouchableHighlight, View} from 'react-native'; - -const styles = StyleSheet.create({ - item: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - height: 40, - paddingHorizontal: 10 - } -}); - -export default class RightMenuDrawerItem extends Component { - static propTypes = { - children: PropTypes.node, - onPress: PropTypes.func, - style: View.propTypes.style - } - - render() { - const {onPress, style, children} = this.props; - return ( - - - {children} - - - ); - } -} diff --git a/app/scenes/select_team/select_team.js b/app/scenes/select_team/select_team.js index a5171ea11..65d4745d6 100644 --- a/app/scenes/select_team/select_team.js +++ b/app/scenes/select_team/select_team.js @@ -1,7 +1,7 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React from 'react'; +import React, {PropTypes, PureComponent} from 'react'; import { Image, InteractionManager, @@ -17,16 +17,18 @@ import {GlobalStyles} from 'app/styles'; import logo from 'assets/images/logo.png'; -export default class SelectTeam extends React.Component { +export default class SelectTeam extends PureComponent { static propTypes = { - config: React.PropTypes.object.isRequired, - teams: React.PropTypes.object.isRequired, - myMembers: React.PropTypes.object.isRequired, - subscribeToHeaderEvent: React.PropTypes.func.isRequired, - unsubscribeFromHeaderEvent: React.PropTypes.func.isRequired, - actions: React.PropTypes.shape({ - goBackToChannelView: React.PropTypes.func.isRequired, - handleTeamChange: React.PropTypes.func.isRequired + config: PropTypes.object.isRequired, + teams: PropTypes.object.isRequired, + myMembers: PropTypes.object.isRequired, + subscribeToHeaderEvent: PropTypes.func.isRequired, + unsubscribeFromHeaderEvent: PropTypes.func.isRequired, + actions: PropTypes.shape({ + closeDrawers: PropTypes.func.isRequired, + closeModal: PropTypes.func.isRequired, + goBack: PropTypes.func.isRequired, + handleTeamChange: PropTypes.func.isRequired }).isRequired }; @@ -48,7 +50,7 @@ export default class SelectTeam extends React.Component { }; componentWillMount() { - this.props.subscribeToHeaderEvent('close', this.props.actions.goBackToChannelView); + this.props.subscribeToHeaderEvent('close', this.props.actions.goBack); } componentWillUnmount() { @@ -57,23 +59,24 @@ export default class SelectTeam extends React.Component { onSelectTeam = async (team) => { const { - goBackToChannelView, + closeDrawers, + closeModal, handleTeamChange } = this.props.actions; - await handleTeamChange(team); - InteractionManager.runAfterInteractions(goBackToChannelView); + handleTeamChange(team); + closeDrawers(); + InteractionManager.runAfterInteractions(closeModal); }; render() { - const teams = []; - for (const id of Object.keys(this.props.teams)) { - const team = this.props.teams[id]; - - if (this.props.myMembers.hasOwnProperty(id)) { - teams.push( + const content = []; + const teams = Object.values(this.props.teams); + for (const team of teams) { + if (this.props.myMembers.hasOwnProperty(team.id)) { + content.push(