From 688d6579d0c388abf8d0d2c4101c117b31948a22 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 5 Sep 2018 10:04:59 -0300 Subject: [PATCH 01/15] Fix custom emoji render (#2075) --- app/components/emoji/emoji.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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); } } From c13081ad340715da7671aa06b357053f09c60b0b Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 5 Sep 2018 10:35:28 -0300 Subject: [PATCH 02/15] Display trigger word in slash command autocomplete (#2078) --- .../autocomplete/slash_suggestion/slash_suggestion.js | 1 - .../autocomplete/slash_suggestion/slash_suggestion_item.js | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) 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} ); From 532e87f6dc589b5e6e5ecd92a110b1083d59dfd2 Mon Sep 17 00:00:00 2001 From: Sudheer Date: Thu, 6 Sep 2018 19:56:26 +0530 Subject: [PATCH 03/15] MM-11942 Add deactivate user state to more users modal (#2079) --- .../__snapshots__/user_list_test.test.js.snap | 1378 +++++++++++++++++ .../user_list_row/user_list_row.js | 11 +- .../user_list_row/user_list_test.test.js | 89 ++ 3 files changed, 1475 insertions(+), 3 deletions(-) create mode 100644 app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap create mode 100644 app/components/custom_list/user_list_row/user_list_test.test.js 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(); + }); +}); From 9bd46b48ce07853398160323767b4835ab21e3eb Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Fri, 7 Sep 2018 01:46:07 +0800 Subject: [PATCH 04/15] add bottom margin of 10 to "show more/less button" (#2081) --- .../__snapshots__/show_more_button.test.js.snap | 4 ++++ app/components/show_more_button/show_more_button.js | 1 + 2 files changed, 5 insertions(+) diff --git a/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap b/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap index 283dae457..f355fdfac 100644 --- a/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap +++ b/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap @@ -100,6 +100,7 @@ ShallowWrapper { "flex": 1, "flexDirection": "row", "justifyContent": "center", + "marginBottom": 10, "position": "relative", "top": 10, } @@ -291,6 +292,7 @@ ShallowWrapper { "flex": 1, "flexDirection": "row", "justifyContent": "center", + "marginBottom": 10, "position": "relative", "top": 10, }, @@ -510,6 +512,7 @@ ShallowWrapper { "flex": 1, "flexDirection": "row", "justifyContent": "center", + "marginBottom": 10, "position": "relative", "top": 10, } @@ -701,6 +704,7 @@ ShallowWrapper { "flex": 1, "flexDirection": "row", "justifyContent": "center", + "marginBottom": 10, "position": "relative", "top": 10, }, diff --git a/app/components/show_more_button/show_more_button.js b/app/components/show_more_button/show_more_button.js index 48e783378..4811e9259 100644 --- a/app/components/show_more_button/show_more_button.js +++ b/app/components/show_more_button/show_more_button.js @@ -101,6 +101,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme, showMore) => { flexDirection: 'row', position: 'relative', top: showMore ? -7.5 : 10, + marginBottom: 10, }, dividerLeft: { backgroundColor: changeOpacity(theme.centerChannelColor, 0.2), From 57a2d3103d05774f0d5756cea605ff4e31370d98 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Thu, 6 Sep 2018 13:46:23 -0400 Subject: [PATCH 05/15] MM-11968 Fix uppercase at mentions (#2082) --- app/components/at_mention/at_mention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)) { From b1dff8aa04f59f63243445371e102db981345396 Mon Sep 17 00:00:00 2001 From: Dmitry Samuylov Date: Thu, 6 Sep 2018 14:04:07 -0400 Subject: [PATCH 06/15] enable arrows on the calendar for date search flags (#2084) --- app/components/autocomplete/date_suggestion/date_suggestion.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/autocomplete/date_suggestion/date_suggestion.js b/app/components/autocomplete/date_suggestion/date_suggestion.js index faf57ed8a..4d247e036 100644 --- a/app/components/autocomplete/date_suggestion/date_suggestion.js +++ b/app/components/autocomplete/date_suggestion/date_suggestion.js @@ -90,7 +90,7 @@ export default class DateSuggestion extends PureComponent { futureScrollRange={0} scrollingEnabled={true} pagingEnabled={true} - hideArrows={true} + hideArrows={false} horizontal={true} showScrollIndicator={true} onDayPress={this.completeMention} From c6ea5e6024aa681db160e03ab5f3fde2adc70800 Mon Sep 17 00:00:00 2001 From: Sudheer Date: Fri, 7 Sep 2018 05:05:50 +0530 Subject: [PATCH 07/15] MM-11603 Add a retry screen when fetching teams fails (#2085) --- app/actions/views/select_team.js | 3 + app/components/root/root.js | 32 +++- app/constants/navigation.js | 1 + .../error_teams_list.test.js.snap | 166 ++++++++++++++++++ .../error_teams_list/error_teams_list.js | 116 ++++++++++++ .../error_teams_list/error_teams_list.test.js | 65 +++++++ app/screens/error_teams_list/index.js | 24 +++ app/screens/index.js | 1 + 8 files changed, 401 insertions(+), 7 deletions(-) create mode 100644 app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap create mode 100644 app/screens/error_teams_list/error_teams_list.js create mode 100644 app/screens/error_teams_list/error_teams_list.test.js create mode 100644 app/screens/error_teams_list/index.js 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/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/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); } From db898694a5ee48e0661f9ca048aa30f951a33809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Fri, 7 Sep 2018 11:51:36 +0200 Subject: [PATCH 08/15] MM-11725: Filter archived channels by membership on search autocomplete (#2066) * MM-11725: Filter archived channels by membership on search autocomplete * Fixing PR suggestions --- .../channel_mention/channel_mention.js | 9 ++++++--- .../autocomplete/channel_mention/index.js | 4 ++++ app/selectors/autocomplete.js | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) 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/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; + }, [])); + } +); From 9ca10dc71977b191b51aed2706f5c84139ac2564 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 10:06:22 -0300 Subject: [PATCH 09/15] Make sure opengraph displays images (#2083) * Make sure opengraph displays images * Revert change for IMAGE_MAX_HEIGHT constant --- .../post_attachment_opengraph.js | 62 ++++++++++++------- app/utils/image_cache_manager.js | 46 +++++++------- 2 files changed, 63 insertions(+), 45 deletions(-) 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/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); } }; From 8e9146e5c905d823eab32830f94ca1907a6cda3a Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 11:35:19 -0300 Subject: [PATCH 10/15] fix contextOption for post (#2086) * fix contextOption for post * feedback review --- .../options_context/options_context.ios.js | 35 +++++++++---------- app/components/post/post.js | 8 +---- 2 files changed, 18 insertions(+), 25 deletions(-) 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) => { From 92f161b54109e1404c8f7b68d04bdcf20ea698b7 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 11:40:23 -0300 Subject: [PATCH 11/15] Update fastlane (#2089) --- fastlane/Gemfile.lock | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) 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) From 363418a3414992002ba0aea509cd253f6f3c93ca Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 19:01:11 -0300 Subject: [PATCH 12/15] Update mattermost-redux (#2091) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ecb6c9a1d..3bcbdae1f 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", diff --git a/package.json b/package.json index f5f511838..6b4f0c772 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", From 5d9c3d7b3d61417a57a972ccfe21b580a6e71913 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 19:02:40 -0300 Subject: [PATCH 13/15] Search Fixes (#2090) * MM-11996 date filter * MM-12041 do not reopen keyboard after search --- .../date_suggestion/date_suggestion.js | 37 ++++++++++++++++--- app/components/quick_text_input.js | 4 +- .../search_bar/search_bar.android.js | 12 ++++-- app/screens/search/search.js | 9 +++-- package-lock.json | 5 +-- package.json | 2 +- 6 files changed, 50 insertions(+), 19 deletions(-) diff --git a/app/components/autocomplete/date_suggestion/date_suggestion.js b/app/components/autocomplete/date_suggestion/date_suggestion.js index 4d247e036..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/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/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 { { + 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/package-lock.json b/package-lock.json index 3bcbdae1f..0dbfb1f17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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 6b4f0c772..afd516459 100644 --- a/package.json +++ b/package.json @@ -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", From 22025bcc454fb829ef70c97f73f89b641b1df95f Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 19:27:36 -0300 Subject: [PATCH 14/15] Bump iOS build number to 137 (#2092) --- ios/Mattermost.xcodeproj/project.pbxproj | 4 ++-- ios/Mattermost/Info.plist | 2 +- ios/MattermostShare/Info.plist | 2 +- ios/MattermostTests/Info.plist | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) 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 From 5592391f92c6e857c34abbc39301d6b22c45d7a7 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 7 Sep 2018 19:32:51 -0300 Subject: [PATCH 15/15] Bump Android build number to 137 (#2093) --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 {