PLT-5930 Moved Settings to channel drawer (#376)
This commit is contained in:
parent
913e29e37b
commit
4bb74c065a
19 changed files with 484 additions and 452 deletions
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<TouchableHighlight
|
||||
style={styles.settingsContainer}
|
||||
onPress={() => this.props.actions.goToModalAccountSettings()}
|
||||
onPress={() => this.props.actions.openSettingsModal()}
|
||||
>
|
||||
<AwesomeIcon
|
||||
name='cog'
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {
|
||||
goToModalAccountSettings,
|
||||
goToCreateChannel,
|
||||
openSettingsModal,
|
||||
requestCloseModal,
|
||||
showMoreChannelsModal,
|
||||
showDirectMessagesModal,
|
||||
|
|
@ -33,9 +33,9 @@ function mapDispatchToProps(dispatch) {
|
|||
actions: bindActionCreators({
|
||||
closeDMChannel,
|
||||
goToCreateChannel,
|
||||
goToModalAccountSettings,
|
||||
leaveChannel,
|
||||
markFavorite,
|
||||
openSettingsModal,
|
||||
unmarkFavorite,
|
||||
showOptionsModal,
|
||||
showDirectMessagesModal,
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ const state = {
|
|||
leftDrawerOpen: false,
|
||||
leftDrawerRoute: Routes.ChannelDrawer,
|
||||
rightDrawerOpen: false,
|
||||
rightDrawerRoute: Routes.RightMenuDrawer
|
||||
rightDrawerRoute: null
|
||||
},
|
||||
views: {
|
||||
channel: {
|
||||
|
|
|
|||
|
|
@ -244,7 +244,6 @@ class Router extends React.Component {
|
|||
leftDrawerOpen,
|
||||
leftDrawerRoute,
|
||||
modal,
|
||||
rightDrawerOpen,
|
||||
rightDrawerRoute,
|
||||
routes
|
||||
} = this.props.navigation;
|
||||
|
|
@ -288,10 +287,10 @@ class Router extends React.Component {
|
|||
tweenHandler={this.handleDrawerTween}
|
||||
>
|
||||
<Drawer
|
||||
open={rightDrawerOpen}
|
||||
open={false}
|
||||
type='displace'
|
||||
side='right'
|
||||
disabled={modalVisible}
|
||||
disabled={true}
|
||||
content={rightDrawerContent}
|
||||
tapToClose={true}
|
||||
openDrawerOffset={50}
|
||||
|
|
@ -300,7 +299,7 @@ class Router extends React.Component {
|
|||
panCloseMask={50}
|
||||
panThreshold={0.2}
|
||||
acceptPan={navigationProps.allowMenuSwipe}
|
||||
negotiatePan={true}
|
||||
negotiatePan={false}
|
||||
useInteractionManager={true}
|
||||
tweenHandler={this.handleDrawerTween}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 <ChannelTitle emitter={emitter}/>;
|
||||
},
|
||||
renderRightComponent: (props, emitter) => {
|
||||
return <ChannelMenuButton emitter={emitter}/>;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<View style={{flexDirection: 'column', justifyContent: 'center', alignItems: 'center', flex: 1, opacity: this.state.opacity}}>
|
||||
<TouchableOpacity onPress={() => this.props.emitter('open_right_menu')}>
|
||||
<Icon
|
||||
name='ellipsis-v'
|
||||
size={25}
|
||||
color={this.props.theme.sidebarHeaderTextColor}
|
||||
style={{paddingHorizontal: 20}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ChannelMenuButton);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 (
|
||||
<ScrollView style={Styles.container}>
|
||||
<Divider style={Styles.divider}/>
|
||||
<RightMenuDrawerItem onPress={this.props.actions.goToModalSelectTeam}>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='exchange'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.switch_team'
|
||||
defaultMessage='Team Selection'
|
||||
/>
|
||||
</RightMenuDrawerItem>
|
||||
<Divider style={Styles.divider}/>
|
||||
<RightMenuDrawerItem>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='question'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.help'
|
||||
defaultMessage='Help'
|
||||
/>
|
||||
</RightMenuDrawerItem>
|
||||
<RightMenuDrawerItem onPress={this.openErrorEmail}>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='phone'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.report'
|
||||
defaultMessage='Report a Problem'
|
||||
/>
|
||||
</RightMenuDrawerItem>
|
||||
<RightMenuDrawerItem>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='info'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='navbar_dropdown.about'
|
||||
defaultMessage='About Mattermost'
|
||||
/>
|
||||
</RightMenuDrawerItem>
|
||||
<Divider style={Styles.divider}/>
|
||||
<RightMenuDrawerItem onPress={this.props.actions.logout}>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='sign-out'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.logout'
|
||||
defaultMessage='Logout'
|
||||
/>
|
||||
</RightMenuDrawerItem>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Divider(props) {
|
||||
return <View {...props}/>;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -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 (
|
||||
<TouchableHighlight
|
||||
underlayColor='rgba(255, 255, 255, 0.3)'
|
||||
onPress={onPress}
|
||||
style={style}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
{children}
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
<Button
|
||||
key={id}
|
||||
key={team.id}
|
||||
onPress={() => this.onSelectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
|
|
@ -108,7 +111,7 @@ export default class SelectTeam extends React.Component {
|
|||
id='signup_team.choose'
|
||||
defaultMessage='Your teams:'
|
||||
/>
|
||||
{teams}
|
||||
{content}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux';
|
|||
|
||||
import navigationSceneConnect from '../navigationSceneConnect';
|
||||
|
||||
import {goBack} from 'app/actions/navigation';
|
||||
import {closeDrawers, closeModal, goBack} from 'app/actions/navigation';
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
|
@ -25,7 +25,9 @@ function mapStateToProps(state) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
goBackToChannelView: goBack,
|
||||
closeDrawers,
|
||||
closeModal,
|
||||
goBack,
|
||||
handleTeamChange
|
||||
}, dispatch)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,15 +2,20 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {goBack, goToModalSelectTeam} from 'app/actions/navigation';
|
||||
import {
|
||||
closeDrawers,
|
||||
goBack,
|
||||
goToAccountSettings,
|
||||
goToSelectTeam
|
||||
} from 'app/actions/navigation';
|
||||
import {clearErrors} from 'mattermost-redux/actions/errors';
|
||||
|
||||
import {logout} from 'mattermost-redux/actions/users';
|
||||
import navigationSceneConnect from 'app/scenes/navigationSceneConnect';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import RightMenuDrawer from './right_menu_drawer';
|
||||
import Settings from './settings';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
|
|
@ -25,12 +30,14 @@ function mapStateToProps(state, ownProps) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
closeDrawers,
|
||||
goBack,
|
||||
goToModalSelectTeam,
|
||||
goToAccountSettings,
|
||||
goToSelectTeam,
|
||||
clearErrors,
|
||||
logout
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(RightMenuDrawer);
|
||||
export default navigationSceneConnect(mapStateToProps, mapDispatchToProps)(Settings);
|
||||
169
app/scenes/settings/settings.js
Normal file
169
app/scenes/settings/settings.js
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {
|
||||
InteractionManager,
|
||||
Linking,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
TouchableHighlight,
|
||||
View
|
||||
} from 'react-native';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import SettingsItem from './settings_item';
|
||||
|
||||
export default class Settings extends PureComponent {
|
||||
static propTypes = {
|
||||
errors: PropTypes.array.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
closeDrawers: PropTypes.func.isRequired,
|
||||
goBack: PropTypes.func.isRequired,
|
||||
goToAccountSettings: PropTypes.func.isRequired,
|
||||
goToSelectTeam: PropTypes.func.isRequired,
|
||||
clearErrors: PropTypes.func.isRequired,
|
||||
logout: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
static navigationProps = {
|
||||
renderLeftComponent: (props, emitter, theme) => {
|
||||
const style = getStyleSheet(theme);
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={style.leftComponent}
|
||||
onPress={props.onNavigateBack}
|
||||
underlayColor={changeOpacity(theme.centerChannelColor, 0.9)}
|
||||
>
|
||||
<MaterialIcon
|
||||
size={20}
|
||||
name='close'
|
||||
color={theme.sidebarHeaderTextColor}
|
||||
/>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
logout = () => {
|
||||
const {closeDrawers, logout} = this.props.actions;
|
||||
closeDrawers();
|
||||
InteractionManager.runAfterInteractions(logout);
|
||||
};
|
||||
|
||||
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 {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<View style={style.wrapper}>
|
||||
<SettingsItem
|
||||
defaultMessage='Account Settings'
|
||||
i18nId='sidebar_right_menu.accountSettings'
|
||||
iconName='cog'
|
||||
iconType='fontawesome'
|
||||
onPress={this.props.actions.goToAccountSettings}
|
||||
separator={true}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='Team Selection'
|
||||
i18nId='sidebar_right_menu.switch_team'
|
||||
iconName='ios-people'
|
||||
iconType='ion'
|
||||
onPress={this.props.actions.goToSelectTeam}
|
||||
separator={true}
|
||||
theme={theme}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage='Report a Problem'
|
||||
i18nId='sidebar_right_menu.report'
|
||||
iconName='warning'
|
||||
iconType='material'
|
||||
onPress={this.openErrorEmail}
|
||||
separator={false}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.divider}/>
|
||||
</View>
|
||||
<View style={style.footer}>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Logout'
|
||||
i18nId='sidebar_right_menu.logout'
|
||||
iconName='exit-to-app'
|
||||
iconType='material'
|
||||
isDestructor={true}
|
||||
onPress={this.logout}
|
||||
separator={false}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
leftComponent: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
width: 44
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
|
||||
flex: 1
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1
|
||||
},
|
||||
footer: {
|
||||
height: 51,
|
||||
justifyContent: 'flex-end',
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 20
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
134
app/scenes/settings/settings_item.js
Normal file
134
app/scenes/settings/settings_item.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class SettingsItem extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
i18nId: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string.isRequired,
|
||||
iconType: PropTypes.oneOf(['fontawesome', 'ion', 'material']).isRequired,
|
||||
isDestructor: PropTypes.bool,
|
||||
onPress: PropTypes.func,
|
||||
separator: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isDestructor: false,
|
||||
separator: true
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
defaultMessage,
|
||||
i18nId,
|
||||
iconName,
|
||||
iconType,
|
||||
isDestructor,
|
||||
onPress,
|
||||
separator,
|
||||
theme
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const destructor = {};
|
||||
if (isDestructor) {
|
||||
destructor.color = '#CA3B27';
|
||||
}
|
||||
|
||||
let divider;
|
||||
if (separator) {
|
||||
divider = (<View style={style.divider}/>);
|
||||
}
|
||||
|
||||
let icon;
|
||||
switch (iconType) {
|
||||
case 'fontawesome':
|
||||
icon = (
|
||||
<FontAwesomeIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'ion':
|
||||
icon = (
|
||||
<IonIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'material':
|
||||
icon = (
|
||||
<MaterialIcon
|
||||
name={iconName}
|
||||
style={[style.icon, destructor]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
style={{flex: 1}}
|
||||
>
|
||||
<View style={style.wrapper}>
|
||||
{icon}
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
defaultMessage={defaultMessage}
|
||||
style={[style.label, destructor]}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{divider}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
height: 51,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flexDirection: 'row',
|
||||
height: 50,
|
||||
paddingLeft: 16
|
||||
},
|
||||
icon: {
|
||||
color: theme.linkColor,
|
||||
fontSize: 16
|
||||
},
|
||||
label: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
lineHeight: 19,
|
||||
marginLeft: 8
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1,
|
||||
marginLeft: 16
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1548,6 +1548,7 @@
|
|||
"mobile.server_ping_failed": "Cannot connect to the server. Please check your server URL and internet connection.",
|
||||
"mobile.server_url.invalid_format": "URL must start with http:// or https://",
|
||||
"mobile.routes.saml": "Single SignOn",
|
||||
"mobile.routes.settings": "Settings",
|
||||
"more_channels.close": "Close",
|
||||
"more_channels.create": "Create New Channel",
|
||||
"more_channels.createClick": "Click 'Create New Channel' to make a new one",
|
||||
|
|
|
|||
Loading…
Reference in a new issue