From a2a7a43e924302d802c9da065ef220f7af60f700 Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Fri, 11 Oct 2019 22:14:07 -0400 Subject: [PATCH 01/10] MM-18829 Updated Channel Header SafeAreaView (#3370) Removed the RN SafeAreaView and applied the padding for our iOS X specific setting. --- .../__snapshots__/channel_info.test.js.snap | 1 + .../channel_info_header.test.js.snap | 4809 +++++++++-------- app/screens/channel_info/channel_info.js | 1 + .../channel_info/channel_info_header.js | 190 +- .../channel_info/channel_info_header.test.js | 1 + 5 files changed, 2521 insertions(+), 2481 deletions(-) diff --git a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap index 5982e5be3..fd0cd0029 100644 --- a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap +++ b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap @@ -27,6 +27,7 @@ exports[`channel_info should match snapshot 1`] = ` isArchived={false} isBot={false} isGroupConstrained={false} + isLandscape={false} isTeammateGuest={false} memberCount={2} onPermalinkPress={[Function]} diff --git a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap index e8f47346b..0122d3937 100644 --- a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap +++ b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap @@ -1,502 +1,545 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`channel_info_header should match snapshot 1`] = ` - + - - - - Channel name - - - - - - - - - - - - - - - - - - + type="O" + /> - - + Channel name - + + + + + + + + + + + + + + + + + + + + + `; exports[`channel_info_header should match snapshot when DM and hasGuests and is the teammate 1`] = ` - + + + + + Channel name + + - - - Channel name - + /> - + + + - - + + + - - - - - - - - - - - - - - - - - - - + > + + + + - + + + + + `; exports[`channel_info_header should match snapshot when DM and hasGuests but its me and not the teammate 1`] = ` - + - - - - Channel name - - - - - - - - - - - - - - - - - - + type="D" + /> - - + Channel name - + + + + + + + + + + + + + + + + + + + + + `; exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = ` - + + + + + Channel name + + - - - Channel name - + /> - + + + - - + + + - - - - - - - - - - - - - - - - - - - + > + + + + - + + + + + `; exports[`channel_info_header should match snapshot when is group constrained 1`] = ` - + - - - - Channel name - - - + - - - - - - - - - - - - - - - - - - - - - + Channel name - -`; - -exports[`channel_info_header should match snapshot when public channel and hasGuests 1`] = ` - - - - - Channel name - - - + + - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +`; + +exports[`channel_info_header should match snapshot when public channel and hasGuests 1`] = ` + + + + + Channel name + + + + - - + - + + + + + + + + + + + + + + + + + + + + + `; diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index 9644e0a7f..5519a65a8 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -603,6 +603,7 @@ export default class ChannelInfo extends PureComponent { isTeammateGuest={isTeammateGuest} hasGuests={currentChannelGuestCount > 0} isGroupConstrained={currentChannel.group_constrained} + isLandscape={isLandscape} /> } diff --git a/app/screens/channel_info/channel_info_header.js b/app/screens/channel_info/channel_info_header.js index e77f874d2..4e2fc2fab 100644 --- a/app/screens/channel_info/channel_info_header.js +++ b/app/screens/channel_info/channel_info_header.js @@ -9,7 +9,6 @@ import { Text, TouchableHighlight, View, - SafeAreaView, } from 'react-native'; import {intlShape} from 'react-intl'; @@ -19,6 +18,7 @@ import ChannelIcon from 'app/components/channel_icon'; import FormattedDate from 'app/components/formatted_date'; import FormattedText from 'app/components/formatted_text'; import Markdown from 'app/components/markdown'; +import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; import mattermostManaged from 'app/mattermost_managed'; import BottomSheet from 'app/utils/bottom_sheet'; import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown'; @@ -44,6 +44,7 @@ export default class ChannelInfoHeader extends React.PureComponent { hasGuests: PropTypes.bool.isRequired, isGroupConstrained: PropTypes.bool, timeZone: PropTypes.string, + isLandscape: PropTypes.bool.isRequired, }; static contextTypes = { @@ -142,6 +143,7 @@ export default class ChannelInfoHeader extends React.PureComponent { isBot, isGroupConstrained, timeZone, + isLandscape, } = this.props; const style = getStyleSheet(theme); @@ -152,101 +154,99 @@ export default class ChannelInfoHeader extends React.PureComponent { style.detail; return ( - - - - - - {displayName} - - - {this.renderHasGuestText(style)} - {purpose.length > 0 && - - - - - - - - - } - {header.length > 0 && - - - - - - - - - } - {isGroupConstrained && - - - - } - {creator && - - - - - } + + + + + {displayName} + - + {this.renderHasGuestText(style)} + {purpose.length > 0 && + + + + + + + + + } + {header.length > 0 && + + + + + + + + + } + {isGroupConstrained && + + + + } + {creator && + + + + + } + ); } } diff --git a/app/screens/channel_info/channel_info_header.test.js b/app/screens/channel_info/channel_info_header.test.js index c865d1b3f..94ec048c5 100644 --- a/app/screens/channel_info/channel_info_header.test.js +++ b/app/screens/channel_info/channel_info_header.test.js @@ -43,6 +43,7 @@ describe('channel_info_header', () => { isTeammateGuest: false, hasGuests: false, isGroupConstrained: false, + isLandscape: false, }; test('should match snapshot', async () => { From 55f048ad7685843bec98ca5140d74d8f51283028 Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Sat, 12 Oct 2019 01:30:03 -0400 Subject: [PATCH 02/10] MM-18173 Removed getState from Add Channel Member method (#3371) Removed getState from Add Channel Member method since the Redux store is not looking for it. 3rd param for this method is a postRootID, which is not sent from this request. --- app/actions/views/channel_add_members.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/actions/views/channel_add_members.js b/app/actions/views/channel_add_members.js index a560fb797..f09e06616 100644 --- a/app/actions/views/channel_add_members.js +++ b/app/actions/views/channel_add_members.js @@ -4,9 +4,9 @@ import {addChannelMember} from 'mattermost-redux/actions/channels'; export function handleAddChannelMembers(channelId, members) { - return async (dispatch, getState) => { + return async (dispatch) => { try { - const requests = members.map((m) => dispatch(addChannelMember(channelId, m, getState))); + const requests = members.map((m) => dispatch(addChannelMember(channelId, m))); return await Promise.all(requests); } catch (error) { From ff3ba8ea01440ee425638530798c6343da88455f Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Sat, 12 Oct 2019 01:30:59 -0400 Subject: [PATCH 03/10] MM-18791 (#3347) Added marginHorizontal spacing to the footer for sections. --- app/screens/settings/section.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/screens/settings/section.js b/app/screens/settings/section.js index b132af8d4..b1834cf77 100644 --- a/app/screens/settings/section.js +++ b/app/screens/settings/section.js @@ -87,6 +87,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { }, footer: { marginTop: 10, + marginHorizontal: 15, fontSize: 12, color: changeOpacity(theme.centerChannelColor, 0.5), }, From 0d7d1b7542c60c7bd4173b3252f0efdb2da049db Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Sat, 12 Oct 2019 01:31:55 -0400 Subject: [PATCH 04/10] MM-18054: Removed Commented On from new messages in thread with same poster (#3351) * MM-18054: Added a check for previousPost to use in validating new messages in thread to check if we should display Commented On message. * MM-18054 Updated Updated the check for Commented line to validate the previous thread was one that the post user commente on. * MM-18054 Fixed Eslint Issues Fixed Eslint Issues * MM-18054 Fixed Eslint Issues Missed a space... * MM-18054 Cleanup Cleaned up some code from suggestions --- app/components/post/index.js | 3 +++ app/components/post/post.js | 6 ++++++ app/components/post_header/post_header.js | 14 ++++++++++++-- app/components/post_list/post_list.js | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/components/post/index.js b/app/components/post/index.js index 342fff76a..ae76dd5b6 100644 --- a/app/components/post/index.js +++ b/app/components/post/index.js @@ -41,6 +41,7 @@ function makeMapStateToProps() { return function mapStateToProps(state, ownProps) { const post = ownProps.post || getPost(state, ownProps.postId); const previousPost = getPost(state, ownProps.previousPostId); + const beforePrevPost = getPost(state, ownProps.beforePrevPostId); const myPreferences = getMyPreferences(state); const currentUserId = getCurrentUserId(state); @@ -84,6 +85,8 @@ function makeMapStateToProps() { isFlagged: isPostFlagged(post.id, myPreferences), isCommentMention, isLandscape: isLandscape(state), + previousPostExists: Boolean(previousPost), + beforePrevPostUserId: (beforePrevPost ? beforePrevPost.user_id : null), }; }; } diff --git a/app/components/post/post.js b/app/components/post/post.js index a1a555fb5..1afd68ce4 100644 --- a/app/components/post/post.js +++ b/app/components/post/post.js @@ -68,6 +68,8 @@ export default class Post extends PureComponent { location: PropTypes.string, isBot: PropTypes.bool, isLandscape: PropTypes.bool.isRequired, + previousPostExists: PropTypes.bool, + beforePrevPostUserId: PropTypes.string, }; static defaultProps = { @@ -250,6 +252,8 @@ export default class Post extends PureComponent { skipPinnedHeader, location, isLandscape, + previousPostExists, + beforePrevPostUserId, } = this.props; if (!post) { @@ -299,6 +303,8 @@ export default class Post extends PureComponent { onUsernamePress={onUsernamePress} renderReplies={renderReplies} theme={theme} + previousPostExists={previousPostExists} + beforePrevPostUserId={beforePrevPostUserId} /> ); } diff --git a/app/components/post_header/post_header.js b/app/components/post_header/post_header.js index fdfa461de..db9b20cdb 100644 --- a/app/components/post_header/post_header.js +++ b/app/components/post_header/post_header.js @@ -43,6 +43,9 @@ export default class PostHeader extends PureComponent { isGuest: PropTypes.bool, userTimezone: PropTypes.string, enableTimezone: PropTypes.bool, + previousPostExists: PropTypes.bool, + post: PropTypes.object, + beforePrevPostUserId: PropTypes.string, }; static defaultProps = { @@ -58,11 +61,18 @@ export default class PostHeader extends PureComponent { }; renderCommentedOnMessage = () => { - if (!this.props.renderReplies || !this.props.commentedOnDisplayName) { + const { + beforePrevPostUserId, + commentedOnDisplayName, + post, + previousPostExists, + renderReplies, + theme, + } = this.props; + if (!renderReplies || !commentedOnDisplayName || (!previousPostExists && post.user_id === beforePrevPostUserId)) { return null; } - const {commentedOnDisplayName, theme} = this.props; const style = getStyleSheet(theme); const displayName = commentedOnDisplayName; diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js index 2ec97afee..87f228c82 100644 --- a/app/components/post_list/post_list.js +++ b/app/components/post_list/post_list.js @@ -260,6 +260,7 @@ export default class PostList extends PureComponent { // Remember that the list is rendered with item 0 at the bottom so the "previous" post // comes after this one in the list const previousPostId = index < this.props.postIds.length - 1 ? this.props.postIds[index + 1] : null; + const beforePrevPostId = index < this.props.postIds.length - 2 ? this.props.postIds[index + 2] : null; const nextPostId = index > 0 ? this.props.postIds[index - 1] : null; const postProps = { @@ -274,6 +275,7 @@ export default class PostList extends PureComponent { onPress: this.props.onPostPress, renderReplies: this.props.renderReplies, shouldRenderReplyButton: this.props.shouldRenderReplyButton, + beforePrevPostId, }; if (PostListUtils.isCombinedUserActivityPost(item)) { From b43638b7280365b8fcaf18a0ae002dec875810c4 Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Sat, 12 Oct 2019 01:34:28 -0400 Subject: [PATCH 05/10] MM-18935 (#3346) Added SafeAreaView padding for the SSO screen --- app/screens/sso/index.js | 2 ++ app/screens/sso/sso.js | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/screens/sso/index.js b/app/screens/sso/index.js index f17246b5b..fa9c18f92 100644 --- a/app/screens/sso/index.js +++ b/app/screens/sso/index.js @@ -6,6 +6,7 @@ import {connect} from 'react-redux'; import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {isLandscape} from 'app/selectors/device'; import {setStoreFromLocalData} from 'mattermost-redux/actions/general'; @@ -15,6 +16,7 @@ function mapStateToProps(state) { return { ...state.views.selectServer, theme: getTheme(state), + isLandscape: isLandscape(state), }; } diff --git a/app/screens/sso/sso.js b/app/screens/sso/sso.js index 788a37196..51fb02f38 100644 --- a/app/screens/sso/sso.js +++ b/app/screens/sso/sso.js @@ -18,6 +18,7 @@ import {Client4} from 'mattermost-redux/client'; import {ViewTypes} from 'app/constants'; import Loading from 'app/components/loading'; +import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; import StatusBar from 'app/components/status_bar'; import {resetToChannel} from 'app/actions/navigation'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; @@ -70,6 +71,7 @@ class SSO extends PureComponent { handleSuccessfulLogin: PropTypes.func.isRequired, setStoreFromLocalData: PropTypes.func.isRequired, }).isRequired, + isLandscape: PropTypes.bool.isRequired, }; useWebkit = true; @@ -203,7 +205,7 @@ class SSO extends PureComponent { }; render() { - const {theme} = this.props; + const {theme, isLandscape} = this.props; const {error, messagingEnabled, renderWebView, jsCode} = this.state; const style = getStyleSheet(theme); @@ -236,7 +238,7 @@ class SSO extends PureComponent { } return ( - + {content} From 854f3082c6031f50b061b4094a8c2b00158b9d42 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 15 Oct 2019 00:27:03 -0700 Subject: [PATCH 06/10] Patch react-native-image-picker (#3411) --- .../react-native-image-picker+0.28.1.patch | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 patches/react-native-image-picker+0.28.1.patch diff --git a/patches/react-native-image-picker+0.28.1.patch b/patches/react-native-image-picker+0.28.1.patch new file mode 100644 index 000000000..46b8ac1b5 --- /dev/null +++ b/patches/react-native-image-picker+0.28.1.patch @@ -0,0 +1,30 @@ +diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.m b/node_modules/react-native-image-picker/ios/ImagePickerManager.m +index 28d5870..3f70983 100644 +--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.m ++++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.m +@@ -455,12 +455,19 @@ - (void)imagePickerController:(UIImagePickerController *)picker didFinishPicking + } + + if (videoURL) { // Protect against reported crash +- NSError *error = nil; +- [fileManager moveItemAtURL:videoURL toURL:videoDestinationURL error:&error]; +- if (error) { +- self.callback(@[@{@"error": error.localizedFailureReason}]); +- return; +- } ++ NSError *error = nil; ++ ++ // If we have write access to the source file, move it. Otherwise use copy. ++ if ([fileManager isWritableFileAtPath:[videoURL path]]) { ++ [fileManager moveItemAtURL:videoURL toURL:videoDestinationURL error:&error]; ++ } else { ++ [fileManager copyItemAtURL:videoURL toURL:videoDestinationURL error:&error]; ++ } ++ ++ if (error) { ++ self.callback(@[@{@"error": error.localizedFailureReason}]); ++ return; ++ } + } + } + From ca75e2407b415c21d5c05ac6008a4ac970ec3eed Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Tue, 15 Oct 2019 03:32:10 -0400 Subject: [PATCH 07/10] MM-19286 Disable Haptic Feedback (#3402) * MM-19286 Disable Haptic Feedback Added a check for iOS version <=9 and for iPhones 5/6/SE versions that don't support haptic feedback. * MM-19286 Fixed the Fix Misread the repo for the haptic feedback so fixing the fix. * MM-19286 Removed line in device.js Removed line in device.js --- app/utils/general.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils/general.js b/app/utils/general.js index 4352bec08..6365e9380 100644 --- a/app/utils/general.js +++ b/app/utils/general.js @@ -52,7 +52,7 @@ export function emptyFunction() { // eslint-disable-line no-empty-function export function hapticFeedback(method = 'impactLight') { ReactNativeHapticFeedback.trigger(method, { - enableVibrateFallback: true, + enableVibrateFallback: false, ignoreAndroidSystemSettings: false, }); } From 4dc6a0fe5c1b37582ef1a6ae64437326dae598e1 Mon Sep 17 00:00:00 2001 From: Michael Kochell Date: Tue, 15 Oct 2019 07:52:25 -0600 Subject: [PATCH 08/10] [MM-19307] Make LHS channel item display name not overlap with mention count (#3417) * make channel item display name more narrow, so it does not run into the mention count * Update snapshot --- .../channel_item/__snapshots__/channel_item.test.js.snap | 8 ++++++++ .../main/channels_list/channel_item/channel_item.js | 1 + 2 files changed, 9 insertions(+) diff --git a/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap b/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap index 2987e202c..302538d24 100644 --- a/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap +++ b/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap @@ -87,6 +87,7 @@ exports[`ChannelItem should match snapshot 1`] = ` "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -202,6 +203,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -316,6 +318,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -430,6 +433,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -533,6 +537,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -637,6 +642,7 @@ exports[`ChannelItem should match snapshot for deactivated user and not searchRe "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -745,6 +751,7 @@ exports[`ChannelItem should match snapshot with draft 1`] = ` "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { @@ -851,6 +858,7 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = ` "fontFamily": "Open Sans", "fontSize": 16, "lineHeight": 24, + "maxWidth": "80%", "paddingRight": 10, }, Object { diff --git a/app/components/sidebars/main/channels_list/channel_item/channel_item.js b/app/components/sidebars/main/channels_list/channel_item/channel_item.js index bfdf56f88..9d42e742f 100644 --- a/app/components/sidebars/main/channels_list/channel_item/channel_item.js +++ b/app/components/sidebars/main/channels_list/channel_item/channel_item.js @@ -245,6 +245,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { fontSize: 16, lineHeight: 24, paddingRight: 10, + maxWidth: '80%', flex: 1, alignSelf: 'center', fontFamily: 'Open Sans', From b253bb23e01713b14a9a7dd606e56d11b8d07b6d Mon Sep 17 00:00:00 2001 From: Dean Whillier Date: Tue, 15 Oct 2019 09:54:43 -0400 Subject: [PATCH 09/10] Bump app build number to 239 (#3421) --- android/app/build.gradle | 2 +- ios/Mattermost.xcodeproj/project.pbxproj | 4 ++-- ios/Mattermost/Info.plist | 2 +- ios/MattermostShare/Info.plist | 2 +- ios/MattermostTests/Info.plist | 2 +- ios/NotificationService/Info.plist | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index b3b313464..774ca60e6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -123,7 +123,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57_5" - versionCode 238 + versionCode 239 versionName "1.24.0" multiDexEnabled = true ndk { diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index cffbebf3f..d9dad2a12 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -2856,7 +2856,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 238; + CURRENT_PROJECT_VERSION = 239; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; @@ -2917,7 +2917,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 238; + CURRENT_PROJECT_VERSION = 239; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist index 518277cf6..92c47118f 100644 --- a/ios/Mattermost/Info.plist +++ b/ios/Mattermost/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 238 + 239 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist index 9b9ffcd1b..b3855e6fc 100644 --- a/ios/MattermostShare/Info.plist +++ b/ios/MattermostShare/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.24.0 CFBundleVersion - 238 + 239 NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/ios/MattermostTests/Info.plist b/ios/MattermostTests/Info.plist index 1553e2ccc..5b10c1430 100644 --- a/ios/MattermostTests/Info.plist +++ b/ios/MattermostTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 238 + 239 diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist index 1a5d8a77b..f458ad0fe 100644 --- a/ios/NotificationService/Info.plist +++ b/ios/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.24.0 CFBundleVersion - 238 + 239 NSExtension NSExtensionPointIdentifier From 0f70af7b1636a85cb4630050c032184c4d3f2ffb Mon Sep 17 00:00:00 2001 From: Amit Uttam Date: Wed, 16 Oct 2019 03:51:06 -0300 Subject: [PATCH 10/10] MM-15469 (Android) Alert if shared text is longer than message limit (#3264) * MM-15469 Alert if shared text is longer than message limit One can't manually enter text into the share extension's text box past 4000 characters (the default/defined text size limit). However, this restriction can still be circumvented by sharing a large item of text. In this latter case, the textarea displays just the first 4000 characters, but the value buffer still contains the entire shared text. On hitting "Send", the oversize text can still be shared. This commit implements [MM-15469](https://mattermost.atlassian.net/browse/MM-15469) to better align the Android sharing user experience with the iOS experience. Since a pre-existing i18n string was used, no additional translations are required. Fixes https://github.com/mattermost/mattermost-server/issues/11431 * Add available characters remaining counter in Android Share textarea Shows number of available characters remaining until textarea limit (default = 4000). Similar to iOS experience. **iOS (normal)** ![iOS (normal)](https://user-images.githubusercontent.com/887849/65007527-d6d1f700-d8dc-11e9-9382-7e4b3c6f4f5d.png) **Android (normal)** ![Android (normal)](https://user-images.githubusercontent.com/887849/65007570-f2d59880-d8dc-11e9-9795-ed137e4dd506.jpg) **iOS (past limit)** ![iOS (past limit)](https://user-images.githubusercontent.com/887849/65007638-2b757200-d8dd-11e9-987d-f5e89b2aa0d3.png) **Android (past limit)** ![Android (past limit)](https://user-images.githubusercontent.com/887849/65007720-5e1f6a80-d8dd-11e9-97ba-370fa6c8db58.jpg) * Don't limit size of Android Share extension text input box Pasted shared text was getting cut off after hitting the `MAX_MESSAGE_LENGTH` for shared posts. Alert validation now checks this limit before allowing content to be posted, which made the `maxLength` limit redundant. Context: https://github.com/mattermost/mattermost-mobile/pull/3264#issuecomment-540962933 --- assets/base/i18n/en.json | 2 + .../android/extension_post/extension_post.js | 73 +++++++++++++++--- .../extension_post/extension_post.test.js | 76 +++++++++++++++++++ 3 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 share_extension/android/extension_post/extension_post.test.js diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 057487627..15bb17e4e 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -453,6 +453,8 @@ "mobile.share_extension.error_message": "An error has occurred while using the share extension.", "mobile.share_extension.error_title": "Extension Error", "mobile.share_extension.team": "Team", + "mobile.share_extension.too_long_message": "Character count: {count}/{max}", + "mobile.share_extension.too_long_title": "Message is too long", "mobile.sidebar_settings.permanent": "Permanent Sidebar", "mobile.sidebar_settings.permanent_description": "Keep the sidebar open permanently", "mobile.storage_permission_denied_description": "Upload files to your Mattermost instance. Open Settings to grant Mattermost Read and Write access to files on this device.", diff --git a/share_extension/android/extension_post/extension_post.js b/share_extension/android/extension_post/extension_post.js index b71c13094..ac90bd558 100644 --- a/share_extension/android/extension_post/extension_post.js +++ b/share_extension/android/extension_post/extension_post.js @@ -406,17 +406,34 @@ export default class ExtensionPost extends PureComponent { onPost = () => { const {channelId, files, value} = this.state; const {currentUserId} = this.props; + const {formatMessage} = this.context.intl; - const data = { - channelId, - currentUserId, - files, - token: this.token, - url: this.url, - value, - }; + if (value.length > MAX_MESSAGE_LENGTH) { + Alert.alert( + formatMessage({ + id: 'mobile.share_extension.too_long_title', + defaultMessage: 'Message is too long', + }), + formatMessage({ + id: 'mobile.share_extension.too_long_message', + defaultMessage: 'Character count: {count}/{max}', + }, { + count: value.length, + max: MAX_MESSAGE_LENGTH, + }) + ); + } else { + const data = { + channelId, + currentUserId, + files, + token: this.token, + url: this.url, + value, + }; - this.onClose(data); + this.onClose(data); + } }; renderBody = () => { @@ -435,7 +452,6 @@ export default class ExtensionPost extends PureComponent { { + if (count < 0) { + return styles.textTooLong; + } + return styles.textLengthOk; + } + + renderMessageLengthRemaining = () => { + const {value} = this.state; + const messageLengthRemaining = MAX_MESSAGE_LENGTH - value.length; + + if (value.length === 0) { + return null; + } + + const renderStyle = [styles.messageLengthRemaining, this.lengthCounterColor(messageLengthRemaining)]; + return ( + + {messageLengthRemaining} + + ); + }; + render() { const {formatMessage} = this.context.intl; const {maxFileSize} = this.props; @@ -610,6 +649,7 @@ export default class ExtensionPost extends PureComponent { {this.renderBody()} + {this.renderMessageLengthRemaining()} {this.renderTeamButton()} {this.renderChannelButton()} @@ -654,6 +694,19 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { flex: 1, padding: 15, }, + messageLengthRemaining: { + paddingTop: 5, + paddingBottom: 5, + paddingLeft: 15, + paddingRight: 15, + opacity: 0.5, + }, + textLengthOk: { + color: theme.centerChannelColor, + }, + textTooLong: { + color: theme.errorTextColor, + }, input: { flex: 1, color: theme.centerChannelColor, diff --git a/share_extension/android/extension_post/extension_post.test.js b/share_extension/android/extension_post/extension_post.test.js new file mode 100644 index 000000000..2422be80a --- /dev/null +++ b/share_extension/android/extension_post/extension_post.test.js @@ -0,0 +1,76 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {Alert, PermissionsAndroid} from 'react-native'; +import {shallowWithIntl} from 'test/intl-test-helper'; + +import ExtensionPost from './extension_post'; + +jest.spyOn(Alert, 'alert').mockReturnValue(true); +jest.spyOn(PermissionsAndroid, 'check').mockReturnValue(PermissionsAndroid.RESULTS.GRANTED); + +jest.mock('react-navigation-stack/dist/views/TouchableItem', () => {}); + +jest.mock('NativeModules', () => ({ + MattermostShare: { + close: jest.fn(), + }, + RNKeychainManager: { + SECURITY_LEVEL_ANY: 'ANY', + }, +})); + +jest.mock('app/mattermost_managed', () => ({ + getConfig: jest.fn().mockReturnValue(false), +})); + +const MAX_MESSAGE_LENGTH = 4000; + +describe('ExtensionPost', () => { + const baseProps = { + actions: { + getTeamChannels: jest.fn(), + }, + channelId: 'channel-id', + channels: {}, + currentUserId: 'current-user-id', + maxFileSize: 1024, + navigation: { + setParams: jest.fn(), + }, + teamId: 'team-id', + }; + + const wrapper = shallowWithIntl( + + ); + + const instance = wrapper.instance(); + + const postMessage = (message) => { + wrapper.setState({value: message}); + instance.onPost(); + }; + + test('should show Alert dialog if shared message is longer than maximum length permitted', () => { + const longMessage = 'a'.repeat(MAX_MESSAGE_LENGTH + 1); + postMessage(longMessage); + + expect(Alert.alert).toHaveBeenCalledTimes(1); + }); + + test('should not show Alert dialog if shared message is within maximum length permitted', () => { + const message = 'a'.repeat(MAX_MESSAGE_LENGTH - 1); + postMessage(message); + + expect(Alert.alert).not.toHaveBeenCalled(); + }); + + test('should not show Alert dialog if shared message is at maximum length permitted', () => { + const exactLengthMessage = 'a'.repeat(MAX_MESSAGE_LENGTH); + postMessage(exactLengthMessage); + + expect(Alert.alert).not.toHaveBeenCalled(); + }); +});