RN-239 show error messages for join/delete channel and create dms (#668)

* RN-239 show error messages for join/delete channel and create dms

* Feedback review
This commit is contained in:
enahum 2017-06-26 15:12:14 -04:00 committed by Harrison Healey
parent c36260cdce
commit 6c03d3b8e6
19 changed files with 232 additions and 99 deletions

View file

@ -20,11 +20,13 @@ export function makeDirectChannel(otherUserId) {
if (channel && myMembers[channel.id]) {
toggleDMChannel(otherUserId, 'true')(dispatch, getState);
handleSelectChannel(channel.id)(dispatch, getState);
} else {
const created = await createDirectChannel(currentUserId, otherUserId)(dispatch, getState);
if (created) {
handleSelectChannel(created.id)(dispatch, getState);
}
return true;
}
const created = await createDirectChannel(currentUserId, otherUserId)(dispatch, getState);
if (created) {
handleSelectChannel(created.id)(dispatch, getState);
}
return created;
};
}

View file

@ -1,10 +0,0 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {makeDirectChannel} from 'app/actions/views/more_dms';
export function handleSendMessage(otherUserId) {
return async (dispatch, getState) => {
await makeDirectChannel(otherUserId)(dispatch, getState);
};
}

View file

@ -11,6 +11,7 @@ import {
} from 'react-native';
import Drawer from 'app/components/drawer';
import {alertErrorWithFallback} from 'app/utils/general';
import ChannelsList from './channels_list';
import Swiper from './swiper';
@ -29,15 +30,18 @@ export default class ChannelDrawer extends PureComponent {
viewChannel: PropTypes.func.isRequired,
makeDirectChannel: PropTypes.func.isRequired,
markChannelAsRead: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setChannelLoading: PropTypes.func.isRequired
}).isRequired,
blurPostTextBox: PropTypes.func.isRequired,
children: PropTypes.node,
channels: PropTypes.object,
currentChannel: PropTypes.object,
currentDisplayName: PropTypes.string,
channelMembers: PropTypes.object,
currentTeam: PropTypes.object,
currentUserId: PropTypes.string.isRequired,
intl: PropTypes.object.isRequired,
myTeamMembers: PropTypes.object.isRequired,
navigator: PropTypes.object,
theme: PropTypes.object.isRequired
@ -162,12 +166,14 @@ export default class ChannelDrawer extends PureComponent {
});
};
joinChannel = (channel) => {
joinChannel = async (channel) => {
const {
actions,
currentChannel,
currentDisplayName,
currentTeam,
currentUserId
currentUserId,
intl
} = this.props;
const {
@ -175,27 +181,46 @@ export default class ChannelDrawer extends PureComponent {
joinChannel,
makeDirectChannel,
markChannelAsRead,
setChannelDisplayName,
setChannelLoading,
viewChannel
} = actions;
markChannelAsRead(currentChannel.id);
setChannelLoading();
viewChannel(currentChannel.id);
setChannelDisplayName(channel.display_name);
const displayValue = {displayName: channel.display_name};
if (channel.type === General.DM_CHANNEL) {
markChannelAsRead(currentChannel.id);
setChannelLoading();
viewChannel(currentChannel.id);
this.closeChannelDrawer();
InteractionManager.runAfterInteractions(() => {
makeDirectChannel(channel.id);
});
const result = await makeDirectChannel(channel.id);
if (result.error) {
const dmFailedMessage = {
id: 'mobile.open_dm.error',
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again."
};
setChannelDisplayName(currentDisplayName);
alertErrorWithFallback(intl, result.error, dmFailedMessage, displayValue);
} else {
this.closeChannelDrawer();
}
} else {
markChannelAsRead(currentChannel.id);
setChannelLoading();
viewChannel(currentChannel.id);
joinChannel(currentUserId, currentTeam.id, channel.id);
this.closeChannelDrawer();
InteractionManager.runAfterInteractions(() => {
handleSelectChannel(channel.id);
});
const result = await joinChannel(currentUserId, currentTeam.id, channel.id);
if (result.error) {
const joinFailedMessage = {
id: 'mobile.join_channel.error',
defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again."
};
setChannelDisplayName(currentDisplayName);
alertErrorWithFallback(intl, result.error, joinFailedMessage, displayValue);
} else {
this.closeChannelDrawer();
InteractionManager.runAfterInteractions(() => {
handleSelectChannel(channel.id);
});
}
}
};

View file

@ -27,8 +27,7 @@ class ChannelDrawerList extends Component {
actions: PropTypes.shape({
makeGroupMessageVisibleIfNecessary: PropTypes.func.isRequired,
searchChannels: PropTypes.func.isRequired,
searchProfiles: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired
searchProfiles: PropTypes.func.isRequired
}).isRequired,
channels: PropTypes.object.isRequired,
channelMembers: PropTypes.object,
@ -40,7 +39,6 @@ class ChannelDrawerList extends Component {
myPreferences: PropTypes.object,
myTeamMembers: PropTypes.object.isRequired,
navigator: PropTypes.object,
onJoinChannel: PropTypes.func.isRequired,
onSearchEnds: PropTypes.func.isRequired,
onSearchStart: PropTypes.func.isRequired,
onSelectChannel: PropTypes.func.isRequired,
@ -88,12 +86,8 @@ class ChannelDrawerList extends Component {
}
onSelectChannel = (channel) => {
const {
makeGroupMessageVisibleIfNecessary,
setChannelDisplayName
} = this.props.actions;
const {makeGroupMessageVisibleIfNecessary} = this.props.actions;
setChannelDisplayName(channel.display_name);
if (channel.type === General.GM_CHANNEL) {
makeGroupMessageVisibleIfNecessary(channel.id);
}

View file

@ -11,8 +11,6 @@ import {getUserIdsInChannels, getUsers, getUserStatuses} from 'mattermost-redux/
import {getGroupChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
import {setChannelDisplayName} from 'app/actions/views/channel';
import FilteredList from './filtered_list';
function mapStateToProps(state, ownProps) {
@ -35,8 +33,7 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
makeGroupMessageVisibleIfNecessary,
searchChannels,
searchProfiles,
setChannelDisplayName
searchProfiles
}, dispatch)
};
}

View file

@ -9,7 +9,7 @@ import {getTeams} from 'mattermost-redux/actions/teams';
import {getChannelsWithUnreadSection, getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeam, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import {handleSelectChannel, setChannelLoading} from 'app/actions/views/channel';
import {handleSelectChannel, setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {getTheme} from 'app/selectors/preferences';
@ -22,6 +22,7 @@ function mapStateToProps(state, ownProps) {
...ownProps,
currentTeam: getCurrentTeam(state),
currentChannel: getCurrentChannel(state),
currentDisplayName: state.views.channel.displayName,
currentUserId,
channels: getChannelsWithUnreadSection(state),
channelMembers: state.entities.channels.myMembers,
@ -39,6 +40,7 @@ function mapDispatchToProps(dispatch) {
viewChannel,
makeDirectChannel,
markChannelAsRead,
setChannelDisplayName,
setChannelLoading
}, dispatch)
};

View file

@ -167,6 +167,7 @@ class Channel extends PureComponent {
const {
currentTeam,
currentChannel,
intl,
navigator,
theme
} = this.props;
@ -186,6 +187,7 @@ class Channel extends PureComponent {
return (
<ChannelDrawer
blurPostTextBox={this.blurPostTextBox}
intl={intl}
navigator={navigator}
>
<StatusBar/>

View file

@ -30,7 +30,7 @@ class ChannelPostList extends PureComponent {
channel: PropTypes.object.isRequired,
channelIsLoading: PropTypes.bool,
channelIsRefreshing: PropTypes.bool,
channelName: PropTypes.string,
currentChannelId: PropTypes.string,
intl: intlShape.isRequired,
loadingPosts: PropTypes.bool,
myMember: PropTypes.object.isRequired,
@ -60,7 +60,7 @@ class ChannelPostList extends PureComponent {
componentWillReceiveProps(nextProps) {
// Show the loader if the channel names change
if (this.props.channelName !== nextProps.channelName) {
if (this.props.currentChannelId !== nextProps.currentChannelId) {
this.setState({
loaderOpacity: new Animated.Value(1)
});
@ -146,7 +146,7 @@ class ChannelPostList extends PureComponent {
const {channelLoaded, loaderOpacity} = this.state;
let component;
if (!posts.length && channel.total_msg_count > 0 && !networkOnline) {
if (!posts.length && channel.total_msg_count > 0 && (!channelIsLoading || !networkOnline)) {
// If no posts has been loaded and we are offline
component = (
<PostListRetry

View file

@ -7,7 +7,7 @@ import {connect} from 'react-redux';
import {selectPost} from 'mattermost-redux/actions/posts';
import {RequestStatus} from 'mattermost-redux/constants';
import {makeGetPostsInChannel} from 'mattermost-redux/selectors/entities/posts';
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentChannelId, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
import {loadPostsIfNecessary, increasePostVisibility} from 'app/actions/views/channel';
import {getTheme} from 'app/selectors/preferences';
@ -19,14 +19,14 @@ function makeMapStateToProps() {
return function mapStateToProps(state, ownProps) {
const channelId = ownProps.channel.id;
const {displayName, refreshing} = state.views.channel;
const {refreshing} = state.views.channel;
const {getPosts} = state.requests.posts;
const posts = getPostsInChannel(state, channelId) || [];
return {
channelIsLoading: (getPosts.status === RequestStatus.STARTED),
channelIsRefreshing: refreshing,
channelName: displayName,
currentChannelId: getCurrentChannelId(state),
posts,
postVisibility: state.views.channel.postVisibility[channelId],
loadingPosts: state.views.channel.loadingPosts[channelId],

View file

@ -13,6 +13,7 @@ import {
import StatusBar from 'app/components/status_bar';
import {preventDoubleTap} from 'app/utils/tap';
import {alertErrorWithFallback} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {General} from 'mattermost-redux/constants';
@ -130,10 +131,23 @@ class ChannelInfo extends PureComponent {
id: 'mobile.channel_info.alertMessageDeleteChannel',
defaultMessage: 'Are you sure you want to delete the {term} {name}?'
};
onPressAction = () => {
this.props.actions.deleteChannel(channel.id).then(() => {
onPressAction = async () => {
const result = await this.props.actions.deleteChannel(channel.id);
if (result.error) {
alertErrorWithFallback(
this.props.intl,
result.error,
{
id: 'mobile.channel_info.delete_failed',
defaultMessage: "We couldn't delete the channel {displayName}. Please check your connection and try again."
},
{
displayName: channel.display_name
}
);
} else {
this.close();
});
}
};
}

View file

@ -6,7 +6,7 @@ import {connect} from 'react-redux';
import {General} from 'mattermost-redux/constants';
import {getChannels, joinChannel, searchChannels} from 'mattermost-redux/actions/channels';
import {getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
import {getChannelsInCurrentTeam, getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
@ -22,7 +22,10 @@ function mapStateToProps(state, ownProps) {
const {getChannels: requestStatus} = state.requests.channels;
const {config, license} = state.entities.general;
const roles = getCurrentUserRoles(state);
const channels = getOtherChannels(state);
const myMembers = getMyChannelMemberships(state);
const channels = getChannelsInCurrentTeam(state).filter((c) => {
return (!myMembers[c.id] && c.type === General.OPEN_CHANNEL);
});
return {
...ownProps,

View file

@ -19,6 +19,7 @@ import ChannelListRow from 'app/components/custom_list/channel_list_row';
import Loading from 'app/components/loading';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
class MoreChannels extends PureComponent {
@ -202,25 +203,42 @@ class MoreChannels extends PureComponent {
};
onSelectChannel = async (id) => {
const {actions, currentTeamId, currentUserId} = this.props;
const {actions, currentTeamId, currentUserId, intl} = this.props;
const {channels} = this.state;
this.emitCanCreateChannel(false);
this.setState({adding: true});
await actions.joinChannel(currentUserId, currentTeamId, id);
const channel = channels.find((c) => c.id === id);
if (channel) {
actions.setChannelDisplayName(channel.display_name);
} else {
actions.setChannelDisplayName('');
}
await actions.handleSelectChannel(id);
const result = await actions.joinChannel(currentUserId, currentTeamId, id);
EventEmitter.emit('close_channel_drawer');
InteractionManager.runAfterInteractions(() => {
this.close();
});
if (result.error) {
alertErrorWithFallback(
intl,
result.error,
{
id: 'mobile.join_channel.error',
defaultMessage: "We couldn't join the channel {displayName}. Please check your connection and try again."
},
{
displayName: channel ? channel.display_name : ''
}
);
this.emitCanCreateChannel(true);
this.setState({adding: false});
} else {
if (channel) {
actions.setChannelDisplayName(channel.display_name);
} else {
actions.setChannelDisplayName('');
}
await actions.handleSelectChannel(id);
EventEmitter.emit('close_channel_drawer');
InteractionManager.runAfterInteractions(() => {
this.close();
});
}
};
onCreateChannel = () => {

View file

@ -14,6 +14,7 @@ import MoreDirectMessages from './more_dms';
function mapStateToProps(state, ownProps) {
const {getProfiles: requestStatus, searchProfiles: searchRequest} = state.requests.users;
const {createChannel: createChannelRequest} = state.requests.channels;
function getUsers() {
const {profiles, currentUserId} = state.entities.users;
@ -32,6 +33,8 @@ function mapStateToProps(state, ownProps) {
preferences: getMyPreferences(state),
profiles: getUsers(),
theme: getTheme(state),
currentDisplayName: state.views.channel.displayName,
createChannelRequest,
requestStatus,
searchRequest
};

View file

@ -18,16 +18,19 @@ import Loading from 'app/components/loading';
import MemberList from 'app/components/custom_list';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
import {createMembersSections, loadingText, renderMemberRow} from 'app/utils/member_list';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
class MoreDirectMessages extends PureComponent {
static propTypes = {
currentDisplayName: PropTypes.string,
intl: intlShape.isRequired,
navigator: PropTypes.object,
preferences: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
profiles: PropTypes.array,
createChannelRequest: PropTypes.object.isRequired,
requestStatus: PropTypes.object.isRequired,
searchRequest: PropTypes.object.isRequired,
actions: PropTypes.shape({
@ -60,11 +63,11 @@ class MoreDirectMessages extends PureComponent {
nextProps.requestStatus.status === RequestStatus.SUCCESS) {
const {page} = this.state;
const profiles = nextProps.profiles.splice(0, (page + 1) * General.PROFILE_CHUNK_SIZE);
this.setState({profiles, showNoResults: true});
this.setState({profiles, showNoResults: true, error: null});
} else if (this.state.searching &&
nextProps.searchRequest.status === RequestStatus.SUCCESS) {
const results = filterProfilesMatchingTerm(nextProps.profiles, this.state.term);
this.setState({profiles: results, showNoResults: true});
this.setState({profiles: results, showNoResults: true, error: null});
}
}
@ -109,6 +112,7 @@ class MoreDirectMessages extends PureComponent {
this.setState({
searching: false,
term: null,
error: null,
page: 0,
profiles: this.props.profiles
});
@ -132,22 +136,44 @@ class MoreDirectMessages extends PureComponent {
};
onSelectMember = async (id) => {
const {actions, preferences, profiles} = this.props;
const {actions, currentDisplayName, intl, preferences, profiles} = this.props;
const user = profiles.find((p) => p.id === id);
this.setState({adding: true});
// save the current channel display name in case it fails
const currentChannelDisplayName = currentDisplayName;
const userDisplayName = displayUsername(user, preferences);
if (user) {
actions.setChannelDisplayName(displayUsername(user, preferences));
actions.setChannelDisplayName(userDisplayName);
} else {
actions.setChannelDisplayName('');
}
await actions.makeDirectChannel(id);
EventEmitter.emit('close_channel_drawer');
InteractionManager.runAfterInteractions(() => {
this.close();
});
const result = await actions.makeDirectChannel(id);
if (result.error) {
actions.setChannelDisplayName(currentChannelDisplayName);
alertErrorWithFallback(
intl,
result.error,
{
id: 'mobile.open_dm.error',
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again."
},
{
displayName: userDisplayName
}
);
this.setState({adding: false});
} else {
EventEmitter.emit('close_channel_drawer');
InteractionManager.runAfterInteractions(() => {
this.close();
});
}
};
render() {

View file

@ -5,9 +5,10 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {setChannelDisplayName} from 'app/actions/views/channel';
import {handleSendMessage} from 'app/actions/views/user_profile';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {getTheme} from 'app/selectors/preferences';
import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
@ -15,10 +16,14 @@ import UserProfile from './user_profile';
function mapStateToProps(state, ownProps) {
const {config} = state.entities.general;
const {createChannel: createChannelRequest} = state.requests.channels;
return {
navigator: ownProps.navigator,
config,
createChannelRequest,
currentChannel: getCurrentChannel(state),
currentDisplayName: state.views.channel.displayName,
currentUserId: getCurrentUserId(state),
user: state.entities.users.profiles[ownProps.userId],
myPreferences: getMyPreferences(state),
@ -29,7 +34,7 @@ function mapStateToProps(state, ownProps) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
handleSendMessage,
makeDirectChannel,
setChannelDisplayName
}, dispatch)
};

View file

@ -9,28 +9,42 @@ import {
Text,
View
} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
import {getDirectChannelName} from 'mattermost-redux/utils/channel_utils';
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import ProfilePicture from 'app/components/profile_picture';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import UserProfileRow from './user_profile_row';
export default class UserProfile extends PureComponent {
class UserProfile extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
handleSendMessage: PropTypes.func.isRequired,
makeDirectChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired
}).isRequired,
config: PropTypes.object.isRequired,
currentChannel: PropTypes.object.isRequired,
currentDisplayName: PropTypes.string,
currentUserId: PropTypes.string.isRequired,
createChannelRequest: PropTypes.object.isRequired,
intl: intlShape.isRequired,
navigator: PropTypes.object,
myPreferences: PropTypes.object,
theme: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
displaySendMessageOption = () => {
const {currentChannel, currentUserId, user} = this.props;
return currentUserId !== user.id && currentChannel.name !== getDirectChannelName(currentUserId, user.id);
};
getDisplayName = () => {
const {theme, myPreferences, user} = this.props;
const style = createStyleSheet(theme);
@ -60,17 +74,38 @@ export default class UserProfile extends PureComponent {
return null;
};
sendMessage = () => {
const {actions, myPreferences, navigator, user} = this.props;
actions.setChannelDisplayName(displayUsername(user, myPreferences));
actions.handleSendMessage(user.id);
navigator.pop({
animated: true
});
sendMessage = async () => {
const {actions, currentDisplayName, intl, myPreferences, navigator, user} = this.props;
// save the current channel display name in case it fails
const currentChannelDisplayName = currentDisplayName;
const userDisplayName = displayUsername(user, myPreferences);
actions.setChannelDisplayName(userDisplayName);
const result = await actions.makeDirectChannel(user.id);
if (result.error) {
actions.setChannelDisplayName(currentChannelDisplayName);
alertErrorWithFallback(
intl,
result.error,
{
id: 'mobile.open_dm.error',
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again."
},
{
displayName: userDisplayName
}
);
} else {
navigator.pop({
animated: true
});
}
};
render() {
const {config, currentUserId, theme, user} = this.props;
const {config, theme, user} = this.props;
const style = createStyleSheet(theme);
return (
@ -95,14 +130,14 @@ export default class UserProfile extends PureComponent {
{config.ShowEmailAddress === 'true' && this.buildDisplayBlock('email')}
{this.buildDisplayBlock('position')}
</View>
{currentUserId !== user.id &&
<UserProfileRow
action={this.sendMessage}
defaultMessage='Send Message'
icon='paper-plane-o'
textId='mobile.routes.user_profile.send_message'
theme={theme}
/>
{this.displaySendMessageOption() &&
<UserProfileRow
action={this.sendMessage}
defaultMessage='Send Message'
icon='paper-plane-o'
textId='mobile.routes.user_profile.send_message'
theme={theme}
/>
}
</ScrollView>
</View>
@ -152,3 +187,5 @@ const createStyleSheet = makeStyleSheetFromTheme((theme) => {
}
});
});
export default injectIntl(UserProfile);

12
app/utils/general.js Normal file
View file

@ -0,0 +1,12 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {Alert} from 'react-native';
export function alertErrorWithFallback(intl, error, fallback, values) {
let msg = error.message;
if (!msg || msg === 'Network request failed') {
msg = intl.formatMessage(fallback, values);
}
Alert.alert('', msg);
}

View file

@ -1691,6 +1691,7 @@
"mobile.channel_info.alertTitleDeleteChannel": "Delete {term}",
"mobile.channel_info.alertTitleLeaveChannel": "Leave {term}",
"mobile.channel_info.alertYes": "Yes",
"mobile.channel_info.delete_failed": "We couldn't delete the channel {displayName}. Please check your connection and try again.",
"mobile.channel_info.privateChannel": "Private Channel",
"mobile.channel_info.publicChannel": "Public Channel",
"mobile.channel_list.alertMessageLeaveChannel": "Are you sure you want to leave the {term} {name}?",
@ -1728,6 +1729,7 @@
"mobile.intro_messages.DM": "This is the start of your direct message history with {teammate}. Direct messages and files shared here are not shown to people outside this area.",
"mobile.intro_messages.default_message": "This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.",
"mobile.intro_messages.default_welcome": "Welcome to {name}!",
"mobile.join_channel.error": "We couldn't join the channel {displayName}. Please check your connection and try again.",
"mobile.loading_channels": "Loading Channels...",
"mobile.loading_members": "Loading Members...",
"mobile.loading_posts": "Loading Messages...",
@ -1736,6 +1738,7 @@
"mobile.offlineIndicator.connected": "Connected",
"mobile.offlineIndicator.connecting": "Connecting...",
"mobile.offlineIndicator.offline": "No internet connection",
"mobile.open_dm.error": "We couldn't open a direct message with {displayName}. Please check your connection and try again.",
"mobile.post.cancel": "Cancel",
"mobile.post.delete_question": "Are you sure you want to delete this post?",
"mobile.post.delete_title": "Delete Post",

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/86454a4eafa5f5676748cfbfa883539be1e6ce90"
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/b21461562fefc6abe2e31373045059652d814ad1"
dependencies:
deep-equal "1.0.1"
harmony-reflect "1.5.1"