diff --git a/android/app/build.gradle b/android/app/build.gradle
index 886fb4c15..7cc7445e7 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -113,7 +113,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion 21
targetSdkVersion 26
- versionCode 136
+ versionCode 137
versionName "1.12.0"
multiDexEnabled = true
ndk {
diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js
index 3e7d69f71..23ba2e8d1 100644
--- a/app/actions/views/select_team.js
+++ b/app/actions/views/select_team.js
@@ -7,6 +7,7 @@ import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/c
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
+import {RequestStatus} from 'mattermost-redux/constants';
import {NavigationTypes} from 'app/constants';
@@ -56,6 +57,8 @@ export function selectDefaultTeam() {
if (defaultTeam) {
handleTeamChange(defaultTeam.id)(dispatch, getState);
+ } else if (state.requests.teams.getTeams.status === RequestStatus.FAILURE || state.requests.teams.getMyTeams.status === RequestStatus.FAILURE) {
+ EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS);
} else {
EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS);
}
diff --git a/app/components/at_mention/at_mention.js b/app/components/at_mention/at_mention.js
index cbf5744e8..f800ef4a2 100644
--- a/app/components/at_mention/at_mention.js
+++ b/app/components/at_mention/at_mention.js
@@ -80,7 +80,7 @@ export default class AtMention extends React.PureComponent {
};
getUserDetailsFromMentionName(props) {
- let mentionName = props.mentionName;
+ let mentionName = props.mentionName.toLowerCase();
while (mentionName.length > 0) {
if (props.usersByUsername.hasOwnProperty(mentionName)) {
diff --git a/app/components/autocomplete/channel_mention/channel_mention.js b/app/components/autocomplete/channel_mention/channel_mention.js
index 6555f62f8..c510f7074 100644
--- a/app/components/autocomplete/channel_mention/channel_mention.js
+++ b/app/components/autocomplete/channel_mention/channel_mention.js
@@ -24,11 +24,13 @@ export default class ChannelMention extends PureComponent {
listHeight: PropTypes.number,
matchTerm: PropTypes.string,
myChannels: PropTypes.array,
+ myMembers: PropTypes.object,
otherChannels: PropTypes.array,
onChangeText: PropTypes.func.isRequired,
onResultCountChange: PropTypes.func.isRequired,
privateChannels: PropTypes.array,
publicChannels: PropTypes.array,
+ deletedPublicChannels: PropTypes.instanceOf(Set),
requestStatus: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
value: PropTypes.string,
@@ -48,7 +50,7 @@ export default class ChannelMention extends PureComponent {
}
componentWillReceiveProps(nextProps) {
- const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus} = nextProps;
+ const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus, myMembers, deletedPublicChannels} = nextProps;
if ((matchTerm !== this.props.matchTerm && matchTerm === null) || this.state.mentionComplete) {
// if the term changes but is null or the mention has been completed we render this component as null
@@ -74,7 +76,8 @@ export default class ChannelMention extends PureComponent {
if (requestStatus !== RequestStatus.STARTED &&
(myChannels !== this.props.myChannels || otherChannels !== this.props.otherChannels ||
- privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels)) {
+ privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels ||
+ myMembers !== this.props.myMembers || deletedPublicChannels !== this.props.deletedPublicChannels)) {
// if the request is complete and the term is not null we show the autocomplete
const sections = [];
if (isSearch) {
@@ -82,7 +85,7 @@ export default class ChannelMention extends PureComponent {
sections.push({
id: 'suggestion.search.public',
defaultMessage: 'Public Channels',
- data: publicChannels,
+ data: publicChannels.filter((cId) => !deletedPublicChannels.has(cId) || myMembers[cId]),
key: 'publicChannels',
});
}
diff --git a/app/components/autocomplete/channel_mention/index.js b/app/components/autocomplete/channel_mention/index.js
index c83f2d6ce..57cea375c 100644
--- a/app/components/autocomplete/channel_mention/index.js
+++ b/app/components/autocomplete/channel_mention/index.js
@@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {searchChannels} from 'mattermost-redux/actions/channels';
+import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {
@@ -12,6 +13,7 @@ import {
filterOtherChannels,
filterPublicChannels,
filterPrivateChannels,
+ getDeletedPublicChannelsIds,
getMatchTermForChannelMention,
} from 'app/selectors/autocomplete';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
@@ -38,8 +40,10 @@ function mapStateToProps(state, ownProps) {
return {
myChannels,
+ myMembers: getMyChannelMemberships(state),
otherChannels,
publicChannels,
+ deletedPublicChannels: getDeletedPublicChannelsIds(state),
privateChannels,
currentTeamId: getCurrentTeamId(state),
matchTerm,
diff --git a/app/components/autocomplete/date_suggestion/date_suggestion.js b/app/components/autocomplete/date_suggestion/date_suggestion.js
index faf57ed8a..0192c3a24 100644
--- a/app/components/autocomplete/date_suggestion/date_suggestion.js
+++ b/app/components/autocomplete/date_suggestion/date_suggestion.js
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
-import {Keyboard, StyleSheet} from 'react-native';
+import {Dimensions, Platform, StyleSheet} from 'react-native';
import PropTypes from 'prop-types';
import {CalendarList} from 'react-native-calendars';
@@ -80,8 +80,6 @@ export default class DateSuggestion extends PureComponent {
const currentDate = (new Date()).toDateString();
const calendarStyle = calendarTheme(theme);
- Keyboard.dismiss();
-
return (
);
}
}
+const getDateFontSize = () => {
+ let fontSize = 14;
+
+ if (Platform.OS === 'ios') {
+ const {height, width} = Dimensions.get('window');
+ if (height < 375 || width < 375) {
+ fontSize = 13;
+ }
+ }
+
+ return fontSize;
+};
+
const calendarTheme = memoizeResult((theme) => ({
calendarBackground: theme.centerChannelBg,
monthTextColor: changeOpacity(theme.centerChannelColor, 0.8),
dayTextColor: theme.centerChannelColor,
textSectionTitleColor: changeOpacity(theme.centerChannelColor, 0.25),
'stylesheet.day.basic': {
+ base: {
+ width: 22,
+ height: 22,
+ alignItems: 'center',
+ },
+ text: {
+ marginTop: 0,
+ fontSize: getDateFontSize(),
+ fontWeight: '300',
+ color: theme.centerChannelColor,
+ backgroundColor: 'rgba(255, 255, 255, 0)',
+ lineHeight: 23,
+ },
today: {
backgroundColor: theme.buttonBg,
- width: 33,
- height: 33,
- borderRadius: 16.5,
+ width: 24,
+ height: 24,
+ borderRadius: 12,
},
todayText: {
color: theme.buttonColor,
diff --git a/app/components/autocomplete/slash_suggestion/slash_suggestion.js b/app/components/autocomplete/slash_suggestion/slash_suggestion.js
index edc0a76a6..e30dad6fd 100644
--- a/app/components/autocomplete/slash_suggestion/slash_suggestion.js
+++ b/app/components/autocomplete/slash_suggestion/slash_suggestion.js
@@ -124,7 +124,6 @@ export default class SlashSuggestion extends Component {
renderItem = ({item}) => (
- {`/${displayName || trigger} ${hint}`}
+ {`/${trigger} ${hint}`}
{description}
);
diff --git a/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap b/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap
new file mode 100644
index 000000000..3c2fe827d
--- /dev/null
+++ b/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap
@@ -0,0 +1,1378 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UserListRow should match snapshot 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+ (@user)
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ (@user)
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ (@user)
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "(@user)",
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": "(@user)",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+ (@user)
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ (@user)
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ (@user)
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "(@user)",
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": "(@user)",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ "context": Object {
+ "intl": Object {
+ "formatMessage": [MockFunction],
+ },
+ },
+ },
+}
+`;
+
+exports[`UserListRow should match snapshot for currentUser with (you) populated in list 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": undefined,
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": undefined,
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ "context": Object {
+ "intl": Object {
+ "formatMessage": [MockFunction] {
+ "calls": Array [
+ Array [
+ Object {
+ "defaultMessage": "(@{username} - you)",
+ "id": "mobile.more_dms.you",
+ },
+ Object {
+ "username": "user",
+ },
+ ],
+ ],
+ },
+ },
+ },
+ },
+}
+`;
+
+exports[`UserListRow should match snapshot for deactivated user 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": undefined,
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+ user
+
+
+
+
+
+ ,
+ ],
+ "enabled": true,
+ "id": "21345",
+ "onPress": [Function],
+ "selectable": undefined,
+ "selected": undefined,
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "size": 32,
+ "userId": "21345",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+
+ user
+
+ ,
+
+
+ ,
+ ],
+ "style": Object {
+ "flexDirection": "row",
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children":
+ user
+ ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "user",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ },
+ },
+ "ref": null,
+ "rendered": "user",
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": ,
+ },
+ "ref": null,
+ "rendered": Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": undefined,
+ "ellipsizeMode": "tail",
+ "numberOfLines": 1,
+ "style": Object {
+ "color": undefined,
+ "fontSize": 15,
+ "marginLeft": 5,
+ },
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ "context": Object {
+ "intl": Object {
+ "formatMessage": [MockFunction] {
+ "calls": Array [
+ Array [
+ Object {
+ "defaultMessage": "{displayname} - Deactivated",
+ "id": "more_direct_channels.directchannel.deactivated",
+ },
+ Object {
+ "displayname": "(@user)",
+ },
+ ],
+ ],
+ },
+ },
+ },
+ },
+}
+`;
diff --git a/app/components/custom_list/user_list_row/user_list_row.js b/app/components/custom_list/user_list_row/user_list_row.js
index 6b9b2f466..f98784c67 100644
--- a/app/components/custom_list/user_list_row/user_list_row.js
+++ b/app/components/custom_list/user_list_row/user_list_row.js
@@ -8,13 +8,11 @@ import {
Text,
View,
} from 'react-native';
+import {displayUsername} from 'mattermost-redux/utils/user_utils';
import ProfilePicture from 'app/components/profile_picture';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
-
import CustomListRow from 'app/components/custom_list/custom_list_row';
-import {displayUsername} from 'mattermost-redux/utils/user_utils';
-
export default class UserListRow extends React.PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
@@ -58,6 +56,13 @@ export default class UserListRow extends React.PureComponent {
}, {username});
}
+ if (user.delete_at > 0) {
+ usernameDisplay = formatMessage({
+ id: 'more_direct_channels.directchannel.deactivated',
+ defaultMessage: '{displayname} - Deactivated',
+ }, {displayname: usernameDisplay});
+ }
+
return (
{
+ const original = require.requireActual('app/utils/theme');
+ return {
+ ...original,
+ changeOpacity: jest.fn(),
+ };
+});
+
+jest.mock('rn-fetch-blob', () => ({
+ fs: {
+ dirs: {
+ DocumentDir: () => jest.fn(),
+ CacheDir: () => jest.fn(),
+ },
+ },
+}));
+
+jest.mock('rn-fetch-blob/fs', () => ({
+ dirs: {
+ DocumentDir: () => jest.fn(),
+ CacheDir: () => jest.fn(),
+ },
+}));
+
+describe('UserListRow', () => {
+ const formatMessage = jest.fn();
+ const baseProps = {
+ id: '123455',
+ isMyUser: false,
+ user: {
+ id: '21345',
+ username: 'user',
+ delete_at: 0,
+ },
+ theme: {},
+ teammateNameDisplay: 'test',
+ };
+
+ test('should match snapshot', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage}}},
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot for deactivated user', () => {
+ const deactivatedUser = {
+ id: '21345',
+ username: 'user',
+ delete_at: 100,
+ };
+
+ const newProps = {
+ ...baseProps,
+ user: deactivatedUser,
+ };
+
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage}}},
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot for currentUser with (you) populated in list', () => {
+ const newProps = {
+ ...baseProps,
+ isMyUser: true,
+ };
+
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage}}},
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/app/components/emoji/emoji.js b/app/components/emoji/emoji.js
index dcb6b2126..e6d20a555 100644
--- a/app/components/emoji/emoji.js
+++ b/app/components/emoji/emoji.js
@@ -57,10 +57,10 @@ export default class Emoji extends React.PureComponent {
}
componentWillMount() {
- const {displayTextOnly, imageUrl} = this.props;
+ const {displayTextOnly, emojiName, imageUrl} = this.props;
this.mounted = true;
if (!displayTextOnly && imageUrl) {
- ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
+ ImageCacheManager.cache(`emoji-${emojiName}`, imageUrl, this.setImageUrl);
}
}
@@ -74,7 +74,7 @@ export default class Emoji extends React.PureComponent {
if (!displayTextOnly && imageUrl &&
imageUrl !== this.props.imageUrl) {
- ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
+ ImageCacheManager.cache(`emoji-${emojiName}`, imageUrl, this.setImageUrl);
}
}
diff --git a/app/components/options_context/options_context.ios.js b/app/components/options_context/options_context.ios.js
index 0e89b3f2d..3fd7c88c1 100644
--- a/app/components/options_context/options_context.ios.js
+++ b/app/components/options_context/options_context.ios.js
@@ -27,34 +27,34 @@ export default class OptionsContext extends PureComponent {
};
}
+ handleHide = () => {
+ this.isShowing = false;
+ this.props.toggleSelected(false);
+ };
+
handleHideUnderlay = () => {
if (!this.isShowing) {
- this.props.toggleSelected(false, false);
+ this.props.toggleSelected(false);
}
};
handleShowUnderlay = () => {
- this.props.toggleSelected(true, false);
- };
-
- handleHide = () => {
- this.isShowing = false;
- this.props.toggleSelected(false, this.props.getPostActions().length > 0);
- };
-
- handleShow = () => {
- this.isShowing = this.props.getPostActions().length > 0;
- this.props.toggleSelected(true, this.isShowing);
+ this.show();
+ this.props.toggleSelected(true);
+ this.isShowing = this.state.actions.length > 0;
};
hide = () => {
+ this.setState({
+ actions: this.props.getPostActions(),
+ });
+
if (this.refs.toolTip) {
this.refs.toolTip.hideMenu();
}
- this.setState({
- actions: this.props.getPostActions(),
- });
+ this.isShowing = false;
+ this.props.toggleSelected(false);
};
show = (additionalAction) => {
@@ -74,7 +74,7 @@ export default class OptionsContext extends PureComponent {
};
handlePress = () => {
- this.props.toggleSelected(false, this.props.getPostActions().length > 0);
+ this.props.toggleSelected(false);
this.props.onPress();
};
@@ -87,10 +87,9 @@ export default class OptionsContext extends PureComponent {
actions={this.state.actions}
arrowDirection='down'
longPress={true}
+ onHide={this.handleHide}
onPress={this.handlePress}
underlayColor='transparent'
- onShow={this.handleShow}
- onHide={this.handleHide}
>
{this.props.children}
diff --git a/app/components/post/post.js b/app/components/post/post.js
index 37bea8265..185f911ab 100644
--- a/app/components/post/post.js
+++ b/app/components/post/post.js
@@ -110,10 +110,6 @@ export default class Post extends PureComponent {
this.props.actions.insertToDraft(`@${username} `);
};
- handleEditDisable = () => {
- this.setState({canEdit: false});
- };
-
handlePostDelete = () => {
const {formatMessage} = this.context.intl;
const {actions, currentUserId, post} = this.props;
@@ -313,9 +309,7 @@ export default class Post extends PureComponent {
});
toggleSelected = (selected) => {
- if (!getToolTipVisible()) {
- this.setState({selected});
- }
+ this.setState({selected});
};
handleCopyText = (text) => {
diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
index fc0a221f6..79f331ec0 100644
--- a/app/components/post_attachment_opengraph/post_attachment_opengraph.js
+++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
@@ -13,7 +13,6 @@ import {
View,
} from 'react-native';
-import ProgressiveImage from 'app/components/progressive_image';
import ImageCacheManager from 'app/utils/image_cache_manager';
import {previewImageAtIndex, calculateDimensions} from 'app/utils/images';
import {getNearestPoint} from 'app/utils/opengraph';
@@ -96,10 +95,22 @@ export default class PostAttachmentOpenGraph extends PureComponent {
});
if (imageUrl) {
- ImageCacheManager.cache(null, imageUrl, this.getImageSize);
+ ImageCacheManager.cache(this.getFilename(imageUrl), imageUrl, this.getImageSize);
}
}
+ getFilename = (link) => {
+ let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
+ const extension = filename.split('.').pop();
+
+ if (extension === filename) {
+ const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
+ filename = `${filename}${ext}`;
+ }
+
+ return `og-${filename}`;
+ };
+
getImageSize = (imageUrl) => {
let prefix = '';
if (Platform.OS === 'android') {
@@ -141,13 +152,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
originalWidth,
originalHeight,
} = this.state;
- let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
- const extension = filename.split('.').pop();
-
- if (extension === filename) {
- const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
- filename = `${filename}${ext}`;
- }
+ const filename = this.getFilename(link);
const files = [{
caption: filename,
@@ -175,6 +180,13 @@ export default class PostAttachmentOpenGraph extends PureComponent {
const style = getStyleSheet(theme);
let description = null;
+ let source;
+ if (imageUrl) {
+ source = {
+ uri: imageUrl,
+ };
+ }
+
if (openGraphData.description) {
description = (
@@ -216,19 +228,22 @@ export default class PostAttachmentOpenGraph extends PureComponent {
{description}
{hasImage &&
-
-
-
-
-
+
+
+
+
+
}
);
@@ -266,6 +281,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
color: changeOpacity(theme.centerChannelColor, 0.7),
marginBottom: 10,
},
+ imageContainer: {
+ alignItems: 'center',
+ },
image: {
borderRadius: 3,
},
diff --git a/app/components/quick_text_input.js b/app/components/quick_text_input.js
index 85c8fba5e..85c38f881 100644
--- a/app/components/quick_text_input.js
+++ b/app/components/quick_text_input.js
@@ -25,9 +25,11 @@ export default class QuickTextInput extends React.PureComponent {
* The string value displayed in this input
*/
value: PropTypes.string.isRequired,
+ refocusInput: PropTypes.bool,
};
static defaultProps = {
+ refocusInput: true,
delayInputUpdate: false,
editable: true,
value: '',
@@ -57,7 +59,7 @@ export default class QuickTextInput extends React.PureComponent {
});
}
- this.hadFocus = this.input.isFocused();
+ this.hadFocus = this.input.isFocused() && this.props.refocusInput;
}
componentDidUpdate(prevProps, prevState) {
diff --git a/app/components/root/root.js b/app/components/root/root.js
index a4438a0f9..5566e7335 100644
--- a/app/components/root/root.js
+++ b/app/components/root/root.js
@@ -30,6 +30,7 @@ export default class Root extends PureComponent {
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
EventEmitter.on(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
EventEmitter.on(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
+ EventEmitter.on(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
}
}
@@ -44,6 +45,7 @@ export default class Root extends PureComponent {
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
EventEmitter.off(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
EventEmitter.off(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
+ EventEmitter.off(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
}
}
@@ -69,11 +71,23 @@ export default class Root extends PureComponent {
setTimeout(this.handleNoTeams, 200);
return;
}
+ this.navigateToTeamsPage('SelectTeam');
+ };
+ errorTeamsList = () => {
+ if (!this.refs.provider) {
+ setTimeout(this.errorTeamsList, 200);
+ return;
+ }
+ this.navigateToTeamsPage('ErrorTeamsList');
+ }
+
+ navigateToTeamsPage = (screen) => {
const {currentUrl, navigator, theme} = this.props;
const {intl} = this.refs.provider.getChildContext();
let navigatorButtons;
+ let passProps = {theme};
if (Platform.OS === 'android') {
navigatorButtons = {
rightButtons: [{
@@ -93,8 +107,16 @@ export default class Root extends PureComponent {
};
}
+ if (screen === 'SelectTeam') {
+ passProps = {
+ ...passProps,
+ currentUrl,
+ userWithoutTeams: true,
+ };
+ }
+
navigator.resetTo({
- screen: 'SelectTeam',
+ screen,
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
animated: false,
backButtonTitle: '',
@@ -105,13 +127,9 @@ export default class Root extends PureComponent {
screenBackgroundColor: theme.centerChannelBg,
},
navigatorButtons,
- passProps: {
- currentUrl,
- userWithoutTeams: true,
- theme,
- },
+ passProps,
});
- };
+ }
handleNotificationTapped = async () => {
const {navigator} = this.props;
diff --git a/app/components/search_bar/search_bar.android.js b/app/components/search_bar/search_bar.android.js
index e3fb3c82d..63aa64fa1 100644
--- a/app/components/search_bar/search_bar.android.js
+++ b/app/components/search_bar/search_bar.android.js
@@ -70,6 +70,7 @@ export default class SearchBarAndroid extends PureComponent {
this.state = {
value: props.value,
isFocused: false,
+ refocusInput: true,
};
}
@@ -93,10 +94,12 @@ export default class SearchBarAndroid extends PureComponent {
onSearchButtonPress = () => {
const {value} = this.props;
-
- if (value) {
- this.props.onSearchButtonPress(value);
- }
+ this.setState({refocusInput: false}, () => {
+ if (value) {
+ this.props.onSearchButtonPress(value);
+ }
+ this.setState({refocusInput: true});
+ });
};
onCancelButtonPress = () => {
@@ -215,6 +218,7 @@ export default class SearchBarAndroid extends PureComponent {
{
flexDirection: 'row',
position: 'relative',
top: showMore ? -7.5 : 10,
+ marginBottom: 10,
},
dividerLeft: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
diff --git a/app/constants/navigation.js b/app/constants/navigation.js
index e781be3c3..809997da3 100644
--- a/app/constants/navigation.js
+++ b/app/constants/navigation.js
@@ -8,6 +8,7 @@ const NavigationTypes = keyMirror({
NAVIGATION_CLOSE_MODAL: null,
NAVIGATION_NO_TEAMS: null,
RESTART_APP: null,
+ NAVIGATION_ERROR_TEAMS: null,
});
export default NavigationTypes;
diff --git a/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap
new file mode 100644
index 000000000..ddb3c9ca7
--- /dev/null
+++ b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap
@@ -0,0 +1,166 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ErrorTeamsList should match snapshot 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": undefined,
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {},
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "errorDescription": Object {
+ "defaultMessage": "Make sure you have an active connection and try again.",
+ "id": "mobile.failed_network_action.shortDescription",
+ },
+ "errorTitle": Object {
+ "defaultMessage": "Team Not Found",
+ "id": "error.team_not_found.title",
+ },
+ "onRetry": [Function],
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": undefined,
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {},
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "errorDescription": Object {
+ "defaultMessage": "Make sure you have an active connection and try again.",
+ "id": "mobile.failed_network_action.shortDescription",
+ },
+ "errorTitle": Object {
+ "defaultMessage": "Team Not Found",
+ "id": "error.team_not_found.title",
+ },
+ "onRetry": [Function],
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ },
+}
+`;
diff --git a/app/screens/error_teams_list/error_teams_list.js b/app/screens/error_teams_list/error_teams_list.js
new file mode 100644
index 000000000..a01257b06
--- /dev/null
+++ b/app/screens/error_teams_list/error_teams_list.js
@@ -0,0 +1,116 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ InteractionManager,
+ View,
+} from 'react-native';
+
+import FailedNetworkAction from 'app/components/failed_network_action';
+import Loading from 'app/components/loading';
+import StatusBar from 'app/components/status_bar';
+import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
+
+const errorTitle = {
+ id: 'error.team_not_found.title',
+ defaultMessage: 'Team Not Found',
+};
+
+const errorDescription = {
+ id: 'mobile.failed_network_action.shortDescription',
+ defaultMessage: 'Make sure you have an active connection and try again.',
+};
+
+export default class ErrorTeamsList extends PureComponent {
+ static propTypes = {
+ actions: PropTypes.shape({
+ loadMe: PropTypes.func.isRequired,
+ connection: PropTypes.func.isRequired,
+ logout: PropTypes.func.isRequired,
+ selectDefaultTeam: PropTypes.func.isRequired,
+ }).isRequired,
+ navigator: PropTypes.object,
+ theme: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+ props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
+
+ this.state = {
+ loading: false,
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.theme !== nextProps.theme) {
+ setNavigatorStyles(this.props.navigator, nextProps.theme);
+ }
+ }
+
+ goToChannelView = () => {
+ const {navigator, theme} = this.props;
+
+ navigator.resetTo({
+ screen: 'Channel',
+ animated: false,
+ navigatorStyle: {
+ navBarHidden: true,
+ statusBarHidden: false,
+ statusBarHideWithNavBar: false,
+ screenBackgroundColor: theme.centerChannelBg,
+ },
+ });
+ };
+
+ getUserInfo = async () => {
+ this.setState({loading: true});
+ this.props.actions.connection(true);
+ await this.props.actions.loadMe();
+ this.props.actions.connection(false);
+ this.setState({loading: false});
+ this.props.actions.selectDefaultTeam();
+ this.goToChannelView();
+ }
+
+ onNavigatorEvent = (event) => {
+ if (event.type === 'NavBarButtonPress') {
+ const {logout} = this.props.actions;
+ if (event.id === 'logout') {
+ InteractionManager.runAfterInteractions(logout);
+ }
+ }
+ };
+
+ render() {
+ const {theme} = this.props;
+ const styles = getStyleSheet(theme);
+
+ if (this.state.loading) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ backgroundColor: theme.centerChannelBg,
+ flex: 1,
+ },
+ };
+});
diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js
new file mode 100644
index 000000000..11a2bfc49
--- /dev/null
+++ b/app/screens/error_teams_list/error_teams_list.test.js
@@ -0,0 +1,65 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {configure, shallow} from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import FailedNetworkAction from 'app/components/failed_network_action';
+import ErrorTeamsList from './error_teams_list';
+
+configure({adapter: new Adapter()});
+
+describe('ErrorTeamsList', () => {
+ const navigator = {
+ setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function
+ };
+
+ const loadMe = async () => {
+ return {
+ data: {},
+ };
+ };
+
+ const baseProps = {
+ actions: {
+ loadMe: () => {}, // eslint-disable-line no-empty-function
+ connection: () => {}, // eslint-disable-line no-empty-function
+ logout: () => {}, // eslint-disable-line no-empty-function
+ selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function
+ },
+ theme: {},
+ navigator,
+ };
+
+ test('should match snapshot', () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should call for userInfo on retry', async () => {
+ const connection = jest.fn();
+ const selectDefaultTeam = jest.fn();
+ const logout = jest.fn();
+ const actions = {
+ loadMe,
+ logout,
+ selectDefaultTeam,
+ connection,
+ };
+
+ const newProps = {
+ ...baseProps,
+ actions,
+ };
+
+ const wrapper = shallow(
+
+ );
+
+ wrapper.find(FailedNetworkAction).props().onRetry();
+ await loadMe();
+ expect(selectDefaultTeam).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/app/screens/error_teams_list/index.js b/app/screens/error_teams_list/index.js
new file mode 100644
index 000000000..31424ed03
--- /dev/null
+++ b/app/screens/error_teams_list/index.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {logout, loadMe} from 'mattermost-redux/actions/users';
+import {connection} from 'app/actions/device';
+import {selectDefaultTeam} from 'app/actions/views/select_team';
+
+import ErrorTeamsList from './error_teams_list.js';
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ logout,
+ selectDefaultTeam,
+ connection,
+ loadMe,
+ }, dispatch),
+ };
+}
+
+export default connect(null, mapDispatchToProps)(ErrorTeamsList);
diff --git a/app/screens/index.js b/app/screens/index.js
index 958bc1b77..c6deec65f 100644
--- a/app/screens/index.js
+++ b/app/screens/index.js
@@ -58,5 +58,6 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('TextPreview', () => wrapWithContextProvider(require('app/screens/text_preview').default), store, Provider);
Navigation.registerComponent('Thread', () => wrapWithContextProvider(require('app/screens/thread').default), store, Provider);
Navigation.registerComponent('TimezoneSettings', () => wrapWithContextProvider(require('app/screens/timezone').default), store, Provider);
+ Navigation.registerComponent('ErrorTeamsList', () => wrapWithContextProvider(require('app/screens/error_teams_list').default), store, Provider);
Navigation.registerComponent('UserProfile', () => wrapWithContextProvider(require('app/screens/user_profile').default), store, Provider);
}
diff --git a/app/screens/search/search.js b/app/screens/search/search.js
index 2b3bc2736..434f124db 100644
--- a/app/screens/search/search.js
+++ b/app/screens/search/search.js
@@ -104,7 +104,7 @@ export default class Search extends PureComponent {
}
componentDidUpdate(prevProps) {
- const {searchingStatus: status, recent} = this.props;
+ const {searchingStatus: status, recent, enableDateSuggestion} = this.props;
const {searchingStatus: prevStatus} = prevProps;
const recentLength = recent.length;
const shouldScroll = prevStatus !== status && (status === RequestStatus.SUCCESS || status === RequestStatus.STARTED);
@@ -114,12 +114,13 @@ export default class Search extends PureComponent {
}
if (shouldScroll) {
- requestAnimationFrame(() => {
+ setTimeout(() => {
+ const modifiersCount = enableDateSuggestion ? 5 : 2;
this.refs.list._wrapperListRef.getListRef().scrollToOffset({ //eslint-disable-line no-underscore-dangle
animated: true,
- offset: SECTION_HEIGHT + (2 * MODIFIER_LABEL_HEIGHT) + (recentLength * RECENT_LABEL_HEIGHT) + ((recentLength + 1) * RECENT_SEPARATOR_HEIGHT),
+ offset: SECTION_HEIGHT + (modifiersCount * MODIFIER_LABEL_HEIGHT) + (recentLength * RECENT_LABEL_HEIGHT) + ((recentLength + 1) * RECENT_SEPARATOR_HEIGHT),
});
- });
+ }, 100);
}
}
diff --git a/app/selectors/autocomplete.js b/app/selectors/autocomplete.js
index 8bc92d477..0bfeb74e8 100644
--- a/app/selectors/autocomplete.js
+++ b/app/selectors/autocomplete.js
@@ -242,3 +242,20 @@ export const makeGetMatchTermForDateMention = () => {
return lastMatchTerm;
};
};
+
+export const getDeletedPublicChannelsIds = createSelector(
+ getMyChannels,
+ getOtherChannels,
+ (myChannels, otherChannels) => {
+ const channels = myChannels.filter((c) => {
+ return (c.type === General.OPEN_CHANNEL);
+ }).concat(otherChannels);
+
+ return new Set(channels.reduce((acc, c) => {
+ if (c.delete_at !== 0) {
+ acc.push(c.id);
+ }
+ return acc;
+ }, []));
+ }
+);
diff --git a/app/utils/image_cache_manager.js b/app/utils/image_cache_manager.js
index fa96aa803..be5c58bf9 100644
--- a/app/utils/image_cache_manager.js
+++ b/app/utils/image_cache_manager.js
@@ -27,33 +27,33 @@ export default class ImageCacheManager {
listener(path);
} else {
addListener(uri, listener);
- if (!uri.startsWith('http')) {
+ if (uri.startsWith('http')) {
+ try {
+ const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
+ const options = {
+ session: uri,
+ timeout: 10000,
+ indicator: true,
+ overwrite: true,
+ path,
+ certificate,
+ };
+
+ this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri);
+ if (this.downloadTask.respInfo.respType === 'text') {
+ throw new Error();
+ }
+
+ notifyAll(uri, path);
+ } catch (e) {
+ RNFetchBlob.fs.unlink(path);
+ notifyAll(uri, uri);
+ }
+ } else {
// In case the uri we are trying to cache is already a local file just notify and return
notifyAll(uri, uri);
- return;
}
- try {
- const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
- const options = {
- session: uri,
- timeout: 10000,
- indicator: true,
- overwrite: true,
- path,
- certificate,
- };
-
- this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri);
- if (this.downloadTask.respInfo.respType === 'text') {
- throw new Error();
- }
-
- notifyAll(uri, path);
- } catch (e) {
- RNFetchBlob.fs.unlink(path);
- notifyAll(uri, uri);
- }
unsubscribe(uri);
}
};
diff --git a/fastlane/Gemfile.lock b/fastlane/Gemfile.lock
index 1c738dd26..af7512ca5 100644
--- a/fastlane/Gemfile.lock
+++ b/fastlane/Gemfile.lock
@@ -6,17 +6,17 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.3)
aws-eventstream (1.0.1)
- aws-partitions (1.100.0)
- aws-sdk-core (3.24.1)
+ aws-partitions (1.103.0)
+ aws-sdk-core (3.27.0)
aws-eventstream (~> 1.0)
aws-partitions (~> 1.0)
aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
- aws-sdk-kms (1.7.0)
- aws-sdk-core (~> 3)
+ aws-sdk-kms (1.9.0)
+ aws-sdk-core (~> 3, >= 3.26.0)
aws-sigv4 (~> 1.0)
- aws-sdk-s3 (1.17.0)
- aws-sdk-core (~> 3, >= 3.21.2)
+ aws-sdk-s3 (1.19.0)
+ aws-sdk-core (~> 3, >= 3.26.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.0)
aws-sigv4 (1.0.3)
@@ -41,7 +41,7 @@ GEM
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.3)
- fastlane (2.101.1)
+ fastlane (2.103.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@@ -74,22 +74,23 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
- xcodeproj (>= 1.5.7, < 2.0.0)
- xcpretty (~> 0.2.8)
+ xcodeproj (>= 1.6.0, < 2.0.0)
+ xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-android_change_package_identifier (0.1.0)
fastlane-plugin-android_change_string_app_name (0.1.1)
nokogiri
fastlane-plugin-find_replace_string (0.1.0)
gh_inspector (1.1.3)
- google-api-client (0.23.4)
+ google-api-client (0.23.8)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- googleauth (0.6.4)
+ signet (~> 0.9)
+ googleauth (0.6.6)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.12)
@@ -125,9 +126,9 @@ GEM
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
- rubyzip (1.2.1)
+ rubyzip (1.2.2)
security (0.1.3)
- signet (0.8.1)
+ signet (0.9.1)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
@@ -149,13 +150,13 @@ GEM
unf_ext (0.0.7.5)
unicode-display_width (1.4.0)
word_wrap (1.0.0)
- xcodeproj (1.5.9)
+ xcodeproj (1.6.0)
CFPropertyList (>= 2.3.3, < 4.0)
- atomos (~> 0.1.2)
+ atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
- nanaimo (~> 0.2.5)
- xcpretty (0.2.8)
+ nanaimo (~> 0.2.6)
+ xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index a60eb2048..cdb3e6100 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -2427,7 +2427,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 136;
+ CURRENT_PROJECT_VERSION = 137;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
@@ -2476,7 +2476,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 136;
+ CURRENT_PROJECT_VERSION = 137;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist
index a5f221f54..a7f1bc60e 100644
--- a/ios/Mattermost/Info.plist
+++ b/ios/Mattermost/Info.plist
@@ -34,7 +34,7 @@
CFBundleVersion
- 136
+ 137
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist
index 72fcd2680..16acc87a9 100644
--- a/ios/MattermostShare/Info.plist
+++ b/ios/MattermostShare/Info.plist
@@ -23,7 +23,7 @@
CFBundleShortVersionString
1.12.0
CFBundleVersion
- 136
+ 137
NSAppTransportSecurity
NSAllowsArbitraryLoads
diff --git a/ios/MattermostTests/Info.plist b/ios/MattermostTests/Info.plist
index 1be755e85..afdd52a33 100644
--- a/ios/MattermostTests/Info.plist
+++ b/ios/MattermostTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 136
+ 137
diff --git a/package-lock.json b/package-lock.json
index bc56b932d..7e96c0497 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10178,8 +10178,8 @@
}
},
"mattermost-redux": {
- "version": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
- "from": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
+ "version": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
+ "from": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "1.2.0",
@@ -14712,9 +14712,8 @@
}
},
"react-native-calendars": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/react-native-calendars/-/react-native-calendars-1.20.0.tgz",
- "integrity": "sha512-VlRoDcnEAWYE1JBPBh/Bie6baLQCmtuOGhw7V5yk09Y4j7Hy8BtuZIHh2+LU/TFYso+wEHJAFdj6D0QFttDOlg==",
+ "version": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
+ "from": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
"requires": {
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
diff --git a/package.json b/package.json
index 4dea68aad..7f41d5bff 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"intl": "1.2.5",
"jail-monkey": "1.0.0",
"jsc-android": "216113.0.3",
- "mattermost-redux": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
+ "mattermost-redux": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
"mime-db": "1.33.0",
"moment-timezone": "0.5.21",
"prop-types": "15.6.1",
@@ -26,7 +26,7 @@
"react-native-animatable": "1.2.4",
"react-native-bottom-sheet": "1.0.3",
"react-native-button": "2.3.0",
- "react-native-calendars": "1.20.0",
+ "react-native-calendars": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
"react-native-circular-progress": "0.2.0",
"react-native-cookies": "3.2.0",
"react-native-device-info": "github:enahum/react-native-device-info#a7bb3cff1086780b2c791a3e43e5b826fdf3ab11",