RN-146 Join open teams (#590)
This commit is contained in:
parent
813d2cb334
commit
2309c91f88
9 changed files with 296 additions and 97 deletions
|
|
@ -179,6 +179,7 @@ export default class ChannelDrawer extends PureComponent {
|
|||
<ChannelDrawerTeams
|
||||
closeChannelDrawer={this.closeChannelDrawer}
|
||||
myTeamMembers={myTeamMembers}
|
||||
navigator={navigator}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -567,9 +567,16 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
},
|
||||
settingsContainer: {
|
||||
alignItems: 'center',
|
||||
height: 44,
|
||||
justifyContent: 'center',
|
||||
width: 50
|
||||
width: 50,
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: 46
|
||||
},
|
||||
ios: {
|
||||
height: 44
|
||||
}
|
||||
})
|
||||
},
|
||||
settings: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
|
|
|
|||
|
|
@ -12,33 +12,56 @@ import {
|
|||
TouchableHighlight,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class ChannelDrawerTeams extends PureComponent {
|
||||
class ChannelDrawerTeams extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
handleTeamChange: PropTypes.func.isRequired
|
||||
getTeams: PropTypes.func.isRequired,
|
||||
handleTeamChange: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
canCreateTeams: PropTypes.bool.isRequired,
|
||||
canJoinTeams: PropTypes.bool.isRequired,
|
||||
closeChannelDrawer: PropTypes.func.isRequired,
|
||||
currentChannelId: PropTypes.string,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
joinableTeams: PropTypes.object.isRequired,
|
||||
myTeamMembers: PropTypes.object.isRequired,
|
||||
navigator: PropTypes.object.isRequired,
|
||||
teams: PropTypes.array.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
MaterialIcon.getImageSource('close', 20, props.theme.sidebarHeaderTextColor).
|
||||
then((source) => {
|
||||
this.closeButton = source;
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.actions.getTeams();
|
||||
}
|
||||
|
||||
selectTeam = (team) => {
|
||||
const {actions, closeChannelDrawer, currentTeamId} = this.props;
|
||||
const {actions, closeChannelDrawer, currentChannelId, currentTeamId} = this.props;
|
||||
if (team.id === currentTeamId) {
|
||||
closeChannelDrawer();
|
||||
} else {
|
||||
if (currentChannelId) {
|
||||
actions.markChannelAsRead(currentChannelId);
|
||||
}
|
||||
closeChannelDrawer();
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
actions.handleTeamChange(team);
|
||||
|
|
@ -46,6 +69,34 @@ export default class ChannelDrawerTeams extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
goToSelectTeam = () => {
|
||||
const {currentUrl, intl, navigator, theme} = this.props;
|
||||
|
||||
navigator.showModal({
|
||||
screen: 'SelectTeam',
|
||||
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
|
||||
animationType: 'slide-up',
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
screenBackgroundColor: theme.centerChannelBg
|
||||
},
|
||||
navigatorButtons: {
|
||||
leftButtons: [{
|
||||
id: 'close-teams',
|
||||
icon: this.closeButton
|
||||
}]
|
||||
},
|
||||
passProps: {
|
||||
currentUrl,
|
||||
theme
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderItem = ({item}) => {
|
||||
const {currentTeamId, currentUrl, myTeamMembers, theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
|
@ -121,10 +172,25 @@ export default class ChannelDrawerTeams extends PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {teams, theme} = this.props;
|
||||
const {joinableTeams, teams, theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
let moreAction;
|
||||
if (Object.keys(joinableTeams).length) {
|
||||
moreAction = (
|
||||
<TouchableHighlight
|
||||
style={styles.moreActionContainer}
|
||||
onPress={() => preventDoubleTap(this.goToSelectTeam)}
|
||||
underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
|
||||
>
|
||||
<Text
|
||||
style={styles.moreAction}
|
||||
>
|
||||
{'+'}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
|
|
@ -188,6 +254,23 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
fontSize: 17,
|
||||
fontWeight: 'normal'
|
||||
},
|
||||
moreActionContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 50,
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: 46
|
||||
},
|
||||
ios: {
|
||||
height: 44
|
||||
}
|
||||
})
|
||||
},
|
||||
moreAction: {
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
fontSize: 30
|
||||
},
|
||||
teamWrapper: {
|
||||
marginTop: 20
|
||||
},
|
||||
|
|
@ -250,3 +333,5 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default injectIntl(ChannelDrawerTeams);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {getTeams} from 'mattermost-redux/actions/teams';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentTeamId, getMyTeams} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentTeamId, getJoinableTeams, getMyTeams} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
|
|
@ -27,7 +30,8 @@ function mapStateToProps(state, ownProps) {
|
|||
|
||||
return {
|
||||
canCreateTeams: false,
|
||||
canJoinTeams: false,
|
||||
joinableTeams: getJoinableTeams(state),
|
||||
currentChannelId: getCurrentChannelId(state),
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUrl: removeProtocol(getCurrentUrl(state)),
|
||||
teams: getMyTeams(state).sort(sortTeams.bind(null, (user.locale))),
|
||||
|
|
@ -39,7 +43,9 @@ function mapStateToProps(state, ownProps) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
handleTeamChange
|
||||
getTeams,
|
||||
handleTeamChange,
|
||||
markChannelAsRead
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,36 +7,34 @@ import {connect} from 'react-redux';
|
|||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
|
||||
import {markChannelAsRead} from 'mattermost-redux/actions/channels';
|
||||
import {getTeams, joinTeam} from 'mattermost-redux/actions/teams';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeam, getTeams, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import SelectTeam from './select_team.js';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const allTeams = Object.values(getTeams(state));
|
||||
const myMembers = getTeamMemberships(state);
|
||||
const user = getCurrentUser(state);
|
||||
const myTeams = allTeams.filter((team) => myMembers.hasOwnProperty(team.id));
|
||||
|
||||
function sortTeams(a, b) {
|
||||
return a.display_name.localeCompare(b.display_name, user.locale, {numeric: true});
|
||||
}
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
config: state.entities.general.config,
|
||||
teamsRequest: state.requests.teams.allTeams,
|
||||
teams: myTeams.sort(sortTeams),
|
||||
currentTeam: getCurrentTeam(state),
|
||||
currentChannelId: getCurrentChannelId(state)
|
||||
teamsRequest: state.requests.teams.getTeams,
|
||||
teams: Object.values(getJoinableTeams(state)).sort(sortTeams),
|
||||
currentChannelId: getCurrentChannelId(state),
|
||||
...ownProps
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
getTeams,
|
||||
handleTeamChange,
|
||||
joinTeam,
|
||||
markChannelAsRead
|
||||
}, dispatch)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,107 +4,209 @@
|
|||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Image,
|
||||
FlatList,
|
||||
InteractionManager,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Button from 'react-native-button';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
import logo from 'assets/images/logo.png';
|
||||
import Loading from 'app/components/loading';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class SelectTeam extends PureComponent {
|
||||
static propTypes = {
|
||||
config: PropTypes.object.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
getTeams: PropTypes.func.isRequired,
|
||||
handleTeamChange: PropTypes.func.isRequired,
|
||||
joinTeam: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
currentChannelId: PropTypes.string,
|
||||
currentUrl: PropTypes.string.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
teams: PropTypes.array.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
handleTeamChange: PropTypes.func.isRequired,
|
||||
markChannelAsRead: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
state = {
|
||||
disableButtons: false
|
||||
constructor(props) {
|
||||
super(props);
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
|
||||
this.state = {
|
||||
joining: false
|
||||
};
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'slide-down'
|
||||
});
|
||||
};
|
||||
|
||||
onSelectTeam = async (team) => {
|
||||
if (!this.state.disableButtons) {
|
||||
this.setState({disableButtons: true});
|
||||
const {
|
||||
currentChannelId,
|
||||
handleTeamChange,
|
||||
markChannelAsRead
|
||||
} = this.props.actions;
|
||||
|
||||
if (currentChannelId) {
|
||||
markChannelAsRead(currentChannelId);
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'close-teams') {
|
||||
this.close();
|
||||
}
|
||||
handleTeamChange(team);
|
||||
EventEmitter.emit('close_channel_drawer');
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.props.navigator.dismissModal({
|
||||
animationType: 'slide-down'
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const content = this.props.teams.map((team) => {
|
||||
return (
|
||||
<Button
|
||||
key={team.id}
|
||||
onPress={() => this.onSelectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
disabled={this.state.disableButtons}
|
||||
>
|
||||
{team.display_name}
|
||||
<Icon
|
||||
name='keyboard-arrow-right'
|
||||
size={24}
|
||||
color='#777'
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
onSelectTeam = async (team) => {
|
||||
this.setState({joining: true});
|
||||
const {currentChannelId} = this.props;
|
||||
const {
|
||||
joinTeam,
|
||||
handleTeamChange,
|
||||
markChannelAsRead
|
||||
} = this.props.actions;
|
||||
|
||||
if (currentChannelId) {
|
||||
markChannelAsRead(currentChannelId);
|
||||
}
|
||||
await joinTeam(team.invite_id, team.id);
|
||||
handleTeamChange(team);
|
||||
EventEmitter.emit('close_channel_drawer');
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.close();
|
||||
});
|
||||
};
|
||||
|
||||
renderItem = ({item}) => {
|
||||
const {currentUrl, theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={GlobalStyles.container}>
|
||||
<ScrollView
|
||||
style={{flex: 1}}
|
||||
contentContainerStyle={{alignItems: 'center', paddingVertical: 64}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
<View style={styles.teamWrapper}>
|
||||
<TouchableOpacity
|
||||
onPress={() => preventDoubleTap(this.onSelectTeam, this, item)}
|
||||
>
|
||||
<Image
|
||||
style={GlobalStyles.logo}
|
||||
source={logo}
|
||||
/>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.config.SiteName}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
id='web.root.signup_info'
|
||||
defaultMessage='All team communication in one place, searchable and accessible anywhere'
|
||||
/>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
id='mobile.select_team.choose'
|
||||
defaultMessage='Your teams:'
|
||||
/>
|
||||
{content}
|
||||
</ScrollView>
|
||||
<View style={styles.teamContainer}>
|
||||
<View style={styles.teamIconContainer}>
|
||||
<Text style={styles.teamIcon}>
|
||||
{item.display_name.substr(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.teamNameContainer}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode='tail'
|
||||
style={styles.teamName}
|
||||
>
|
||||
{item.display_name}
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode='tail'
|
||||
style={styles.teamUrl}
|
||||
>
|
||||
{`${currentUrl}/${item.name}`}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {teams, theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
if (this.state.joining) {
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.headingContainer}>
|
||||
<View style={styles.headingWrapper}>
|
||||
<FormattedText
|
||||
id='mobile.select_team.join_open'
|
||||
defaultMessage='Open teams you can join'
|
||||
style={styles.heading}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.line}/>
|
||||
</View>
|
||||
<FlatList
|
||||
data={teams}
|
||||
renderItem={this.renderItem}
|
||||
keyExtractor={(item) => item.id}
|
||||
viewabilityConfig={{
|
||||
viewAreaCoveragePercentThreshold: 3,
|
||||
waitForInteraction: false
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flex: 1
|
||||
},
|
||||
headingContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 16,
|
||||
marginTop: 20
|
||||
},
|
||||
headingWrapper: {
|
||||
marginRight: 15
|
||||
},
|
||||
heading: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 13
|
||||
},
|
||||
line: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
width: '100%',
|
||||
height: StyleSheet.hairlineWidth
|
||||
},
|
||||
teamWrapper: {
|
||||
marginTop: 20
|
||||
},
|
||||
teamContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 16
|
||||
},
|
||||
teamIconContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.buttonBg,
|
||||
borderRadius: 2,
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
width: 40
|
||||
},
|
||||
teamIcon: {
|
||||
color: theme.buttonColor,
|
||||
fontFamily: 'OpenSans',
|
||||
fontSize: 18,
|
||||
fontWeight: '600'
|
||||
},
|
||||
teamNameContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
marginLeft: 10
|
||||
},
|
||||
teamName: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 18
|
||||
},
|
||||
teamUrl: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 12
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1755,7 +1755,7 @@
|
|||
"mobile.routes.thread_dm": "Direct Message Thread",
|
||||
"mobile.routes.user_profile": "Profile",
|
||||
"mobile.routes.user_profile.send_message": "Send Message",
|
||||
"mobile.select_team.choose": "Your teams:",
|
||||
"mobile.select_team.join_open": "Open teams you can join",
|
||||
"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.session_expired": "Session Expired: Please log in to continue receiving notifications.",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation];
|
||||
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3645,7 +3645,7 @@ makeerror@1.0.x:
|
|||
|
||||
mattermost-redux@mattermost/mattermost-redux#master:
|
||||
version "0.0.1"
|
||||
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/17b85854e5fda76d4e91b1e28d1e26b4ad57394d"
|
||||
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/67a40ea6c89a5065b751f434094183956817b1d3"
|
||||
dependencies:
|
||||
deep-equal "1.0.1"
|
||||
harmony-reflect "1.5.1"
|
||||
|
|
|
|||
Loading…
Reference in a new issue