Various fixes & improvements (#611)

* RN-166 follow user prefs for join/leave messages

* RN-158 Fix Android input textbox offscreen issue

* RN-181 Use device locale as default locale

* Upgrade mattermost-redux

* Fix TouchableHighlight in the channel drawer items

* Update channel title when switching channels

* Fix channel name title when switching teams

* Fix unit test
This commit is contained in:
enahum 2017-06-08 15:40:29 -04:00 committed by GitHub
parent f1c9f1051c
commit ecf39f61dd
18 changed files with 94 additions and 82 deletions

View file

@ -345,3 +345,10 @@ export function setPostTooltipVisible(visible = true) {
visible
};
}
export function setChannelDisplayName(displayName) {
return {
type: ViewTypes.SET_CHANNEL_DISPLAY_NAME,
displayName
};
}

View file

@ -5,6 +5,8 @@ import {batchActions} from 'redux-batched-actions';
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
import {setChannelDisplayName} from './channel';
export function handleTeamChange(team) {
return async (dispatch, getState) => {
const {currentTeamId} = getState().entities.teams;
@ -15,6 +17,8 @@ export function handleTeamChange(team) {
const state = getState();
const lastChannelId = state.views.team.lastChannelForTeam[team.id] || '';
dispatch(setChannelDisplayName(''), getState);
dispatch(batchActions([
{type: TeamTypes.SELECT_TEAM, data: team.id},
{type: ChannelTypes.SELECT_CHANNEL, data: lastChannelId}

View file

@ -24,14 +24,20 @@ export default class ChannelDrawerItem extends PureComponent {
theme: PropTypes.object.isRequired
};
onPress = () => {
const {channel, onSelectChannel} = this.props;
setTimeout(() => {
preventDoubleTap(onSelectChannel, this, channel);
}, 100);
};
render() {
const {
channel,
theme,
mentions,
hasUnread,
isActive,
onSelectChannel
isActive
} = this.props;
const style = getStyleSheet(theme);
@ -80,7 +86,7 @@ export default class ChannelDrawerItem extends PureComponent {
return (
<TouchableHighlight
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
onPress={() => preventDoubleTap(onSelectChannel, this, channel)}
onPress={this.onPress}
>
<View style={style.container}>
{activeBorder}

View file

@ -28,6 +28,9 @@ import UnreadIndicator from './unread_indicator';
class ChannelDrawerList extends Component {
static propTypes = {
actions: PropTypes.shape({
setChannelDisplayName: PropTypes.func.isRequired
}).isRequired,
canCreatePrivateChannels: PropTypes.bool.isRequired,
channels: PropTypes.object.isRequired,
channelMembers: PropTypes.object,
@ -98,6 +101,7 @@ class ChannelDrawerList extends Component {
};
onSelectChannel = (channel) => {
this.props.actions.setChannelDisplayName(channel.display_name);
this.props.onSelectChannel(channel.id);
};

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
@ -8,6 +9,8 @@ import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {setChannelDisplayName} from 'app/actions/views/channel';
import ChannelDrawerList from './channel_drawer_list';
function mapStateToProps(state, ownProps) {
@ -20,4 +23,12 @@ function mapStateToProps(state, ownProps) {
};
}
export default connect(mapStateToProps)(ChannelDrawerList);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
setChannelDisplayName
}, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ChannelDrawerList);

View file

@ -134,7 +134,11 @@ class ChannelDrawerTeams extends PureComponent {
<View style={styles.teamWrapper}>
<TouchableHighlight
underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
onPress={() => preventDoubleTap(this.selectTeam, this, item)}
onPress={() => {
setTimeout(() => {
preventDoubleTap(this.selectTeam, this, item);
}, 100);
}}
>
<View style={styles.teamContainer}>
<View style={styles.teamIconContainer}>

View file

@ -11,6 +11,9 @@ export default class Drawer extends BaseDrawer {
onRequestClose: PropTypes.func.isRequired
};
// To fix the android onLayout issue give this a value of 100% as it does not need another one
getHeight = () => '100%';
processTapGestures = () => {
// Note that we explicitly don't support tap to open or double tap because I didn't copy them over

View file

@ -2,11 +2,11 @@
// See License.txt for license information.
import {connect} from 'react-redux';
import DeviceInfo from 'react-native-device-info';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
import {getTheme} from 'app/selectors/preferences';
import Config from 'assets/config.json';
import Root from './root';
@ -14,7 +14,7 @@ function mapStateToProps(state, ownProps) {
const users = state.entities.users;
const {currentUserId} = users;
let locale = Config.DefaultLocale;
let locale = DeviceInfo.getDeviceLocale().split('-')[0];
if (currentUserId && users.profiles[currentUserId]) {
locale = users.profiles[currentUserId].locale;
}

View file

@ -35,6 +35,7 @@ const ViewTypes = keyMirror({
SET_CHANNEL_LOADER: null,
SET_CHANNEL_REFRESHING: null,
SET_CHANNEL_DISPLAY_NAME: null,
POST_TOOLTIP_VISIBLE: null,

View file

@ -6,6 +6,15 @@ import {ChannelTypes, FileTypes} from 'mattermost-redux/action_types';
import {ViewTypes} from 'app/constants';
function displayName(state = '', action) {
switch (action.type) {
case ViewTypes.SET_CHANNEL_DISPLAY_NAME:
return action.displayName;
default:
return state;
}
}
function drafts(state = {}, action) {
switch (action.type) {
case ViewTypes.POST_DRAFT_CHANGED: {
@ -191,6 +200,7 @@ function tooltipVisible(state = false, action) {
}
export default combineReducers({
displayName,
drafts,
loading,
refreshing,

View file

@ -2,19 +2,20 @@
// See License.txt for license information.
import {combineReducers} from 'redux';
import Config from 'assets/config.json';
import DeviceInfo from 'react-native-device-info';
import {UserTypes} from 'mattermost-redux/action_types';
function locale(state = Config.DefaultLocale, action) {
const defaultLocale = DeviceInfo.getDeviceLocale().split('-')[0];
function locale(state = defaultLocale, action) {
switch (action.type) {
case UserTypes.RECEIVED_ME: {
const data = action.data || action.payload;
return data.locale;
}
case UserTypes.LOGOUT_SUCCESS:
return Config.DefaultLocale;
return defaultLocale;
}
return state;

View file

@ -193,10 +193,12 @@ class Channel extends PureComponent {
style={{flex: 1, backgroundColor: theme.centerChannelBg}}
keyboardVerticalOffset={0}
>
<ChannelPostList
channel={currentChannel}
navigator={navigator}
/>
<View style={style.postList}>
<ChannelPostList
channel={currentChannel}
navigator={navigator}
/>
</View>
<PostTextbox
ref={this.attachPostTextbox}
files={channelDraft.files}
@ -238,6 +240,17 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
paddingTop: 20
}
})
},
postList: {
flex: 1,
...Platform.select({
android: {
marginTop: 46
},
ios: {
marginTop: 64
}
})
}
});
});

View file

@ -3,9 +3,9 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {selectPost, getPostsBefore} from 'mattermost-redux/actions/posts';
import {getAllPosts, getPostsInCurrentChannel} from 'mattermost-redux/selectors/entities/posts';
import {makeGetPostsInChannel} from 'mattermost-redux/selectors/entities/posts';
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
import {loadPostsIfNecessary} from 'app/actions/views/channel';
@ -13,58 +13,12 @@ import {getTheme} from 'app/selectors/preferences';
import ChannelPostList from './channel_post_list';
const getPostsInCurrentChannelWithReplyProps = createSelector(
getAllPosts,
getPostsInCurrentChannel,
(allPosts, postsInChannel) => {
const posts = [];
for (let i = 0; i < postsInChannel.length; i++) {
let post = postsInChannel[i];
if (post.root_id) {
let isFirstReply = false;
let isLastReply = false;
let commentedOnPost;
if (i + 1 <= postsInChannel.length) {
const previousPost = postsInChannel[i + 1];
if (previousPost.root_id !== post.root_id) {
isFirstReply = true;
if (previousPost.id !== post.root_id) {
commentedOnPost = allPosts[post.root_id];
}
}
} else {
// The first visible comment will always be the first comment in a thread and will be
// commenting on a post that isn't visible
isFirstReply = true;
commentedOnPost = allPosts[post.root_id];
}
if (i - 1 < 0 || postsInChannel[i - 1].root_id !== post.root_id) {
isLastReply = true;
}
post = {
...post,
isFirstReply,
isLastReply,
commentedOnPost
};
}
posts.push(post);
}
return posts;
}
);
const getPostsInCurrentChannelWithReplyProps = makeGetPostsInChannel();
function mapStateToProps(state, ownProps) {
const {loading, refreshing} = state.views.channel;
const {currentChannelId} = state.entities.channels;
return {
...ownProps,
applicationInitializing: state.views.root.appInitializing,
@ -72,7 +26,7 @@ function mapStateToProps(state, ownProps) {
channelIsRefreshing: refreshing,
myMember: getMyCurrentChannelMembership(state),
postsRequests: state.requests.posts,
posts: getPostsInCurrentChannelWithReplyProps(state),
posts: getPostsInCurrentChannelWithReplyProps(state, currentChannelId) || [],
theme: getTheme(state),
networkOnline: state.offline.online
};

View file

@ -20,7 +20,7 @@ function ChannelTitle(props) {
return null;
}
const channelName = props.currentChannel.display_name;
const channelName = props.displayName || props.currentChannel.display_name;
let icon;
if (channelName) {
icon = (
@ -58,12 +58,14 @@ function ChannelTitle(props) {
ChannelTitle.propTypes = {
applicationInitializing: PropTypes.bool.isRequired,
currentChannel: PropTypes.object,
displayName: PropTypes.string,
onPress: PropTypes.func,
theme: PropTypes.object
};
ChannelTitle.defaultProps = {
currentChannel: {},
displayName: null,
theme: {}
};
@ -71,6 +73,7 @@ function mapStateToProps(state) {
return {
applicationInitializing: state.views.root.appInitializing,
currentChannel: getCurrentChannel(state),
displayName: state.views.channel.displayName,
theme: getTheme(state)
};
}

View file

@ -1,15 +1,11 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React, {PureComponent} from 'react';
import {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import {RequestStatus} from 'mattermost-redux/constants';
import ChannelLoader from 'app/components/channel_loader';
import StatusBar from 'app/components/status_bar';
export default class LoadTeam extends PureComponent {
static propTypes = {
navigator: PropTypes.object,
@ -80,11 +76,6 @@ export default class LoadTeam extends PureComponent {
};
render() {
return (
<View style={{flex: 1}}>
<StatusBar/>
<ChannelLoader theme={this.props.theme}/>
</View>
);
return null;
}
}

View file

@ -1,7 +1,6 @@
{
"DefaultServerUrl": "",
"TestServerUrl": "http://localhost:8065",
"DefaultLocale": "en",
"DefaultTheme": "default",
"ShowErrorsList": false,
"MinServerVersion": "3.8.0"

View file

@ -2,7 +2,8 @@ import {addMock} from 'mocha-react-native';
addMock('react-native-device-info', {
getBuildNumber: () => true,
getVersion: () => true
getVersion: () => true,
getDeviceLocale: () => 'en'
});
addMock('react-native-linear-gradient', {});
addMock('UIManager', {});

View file

@ -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/969e3ef432a077025ae13d3ad917bf5aaf054086"
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/9a2e8099daea436dab76aa612be60492311387b3"
dependencies:
deep-equal "1.0.1"
harmony-reflect "1.5.1"