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:
parent
f1c9f1051c
commit
ecf39f61dd
18 changed files with 94 additions and 82 deletions
|
|
@ -345,3 +345,10 @@ export function setPostTooltipVisible(visible = true) {
|
|||
visible
|
||||
};
|
||||
}
|
||||
|
||||
export function setChannelDisplayName(displayName) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_DISPLAY_NAME,
|
||||
displayName
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ const ViewTypes = keyMirror({
|
|||
|
||||
SET_CHANNEL_LOADER: null,
|
||||
SET_CHANNEL_REFRESHING: null,
|
||||
SET_CHANNEL_DISPLAY_NAME: null,
|
||||
|
||||
POST_TOOLTIP_VISIBLE: null,
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"DefaultServerUrl": "",
|
||||
"TestServerUrl": "http://localhost:8065",
|
||||
"DefaultLocale": "en",
|
||||
"DefaultTheme": "default",
|
||||
"ShowErrorsList": false,
|
||||
"MinServerVersion": "3.8.0"
|
||||
|
|
|
|||
|
|
@ -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', {});
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue