From 8e1a9e8322e3254f581e3b5cf3ed08ac9adf30c1 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Mon, 3 Jun 2019 16:44:28 -0400 Subject: [PATCH 01/19] MM-15774 Add native code to support RNN v2 on iOS (#2848) * Remove fix for MM-9233 * MM-15774 Add native code to support RNN v2 on iOS --- Makefile | 1 - app/mattermost.js | 120 +++++++++------ app/screens/index.js | 137 +++++++++-------- ios/Mattermost.xcodeproj/project.pbxproj | 77 ++++++---- ios/Mattermost/AppDelegate.m | 8 +- ios/Podfile.lock | 2 +- package-lock.json | 182 +++++++++++++---------- package.json | 2 +- 8 files changed, 303 insertions(+), 226 deletions(-) diff --git a/Makefile b/Makefile index 626af86ab..3b1589787 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,6 @@ post-install: @sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-messageformat/package.json @sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json @sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json - @sed -i'' -e "s|super.onBackPressed();|this.moveTaskToBack(true);|g" node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java @sed -i'' -e "s|compile 'com.facebook.react:react-native:0.17.+'|compile 'com.facebook.react:react-native:+'|g" node_modules/react-native-bottom-sheet/android/build.gradle @if [ $(shell grep "const Platform" node_modules/react-native/Libraries/Lists/VirtualizedList.js | grep -civ grep) -eq 0 ]; then \ sed $ -i'' -e "s|const ReactNative = require('ReactNative');|const ReactNative = require('ReactNative');`echo $\\\\\\r;`const Platform = require('Platform');|g" node_modules/react-native/Libraries/Lists/VirtualizedList.js; \ diff --git a/app/mattermost.js b/app/mattermost.js index cb11fe909..9b740bc5e 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -385,41 +385,55 @@ const handleSwitchToDefaultChannel = (teamId) => { }; const launchSelectServer = () => { - Navigation.startSingleScreenApp({ - screen: { - screen: 'SelectServer', - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name: 'SelectServer', + passProps: { + allowOtherServers: app.allowOtherServers, + }, + }, + }], + options: { + layout: { + backgroundColor: 'transparent', + }, + statusBar: { + visible: true, + }, + topBar: { + visible: false, + }, + }, }, }, - passProps: { - allowOtherServers: app.allowOtherServers, - }, - appStyle: { - orientation: 'auto', - }, - animationType: 'fade', }); }; const launchChannel = () => { - Navigation.startSingleScreenApp({ - screen: { - screen: 'Channel', - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name: 'Channel', + }, + }], + options: { + layout: { + backgroundColor: 'transparent', + }, + statusBar: { + visible: true, + }, + topBar: { + visible: false, + }, + }, }, }, - appStyle: { - orientation: 'auto', - }, - animationType: 'fade', }); }; @@ -480,30 +494,40 @@ const handleAppInActive = () => { AppState.addEventListener('change', handleAppStateChange); const launchEntry = () => { - telemetry.start([ - 'start:select_server_screen', - 'start:channel_screen', - ]); + Navigation.events().registerAppLaunchedListener(() => { + telemetry.start([ + 'start:select_server_screen', + 'start:channel_screen', + ]); - Navigation.startSingleScreenApp({ - screen: { - screen: 'Entry', - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name: 'Entry', + passProps: { + initializeModules, + }, + }, + }], + options: { + layout: { + backgroundColor: 'transparent', + }, + statusBar: { + visible: true, + }, + topBar: { + visible: false, + }, + }, + }, }, - }, - passProps: { - initializeModules, - }, - appStyle: { - orientation: 'auto', - }, - animationType: 'fade', - }); + }); - telemetry.startSinceLaunch(['start:splash_screen']); + telemetry.startSinceLaunch(['start:splash_screen']); + }); }; configurePushNotifications(); diff --git a/app/screens/index.js b/app/screens/index.js index b2cfa0072..44865bfbe 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -1,71 +1,90 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import React from 'react'; import {Navigation} from 'react-native-navigation'; import {gestureHandlerRootHOC} from 'react-native-gesture-handler'; import Channel from 'app/screens/channel'; import Entry from 'app/screens/entry'; +import Root from 'app/components/root'; import SelectServer from 'app/screens/select_server'; -import {wrapWithContextProvider} from 'app/utils/wrap_context_provider'; + +// TODO remove dummy navigator object +const navigator = { + push: () => {}, // eslint-disable-line no-empty-function + setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function +}; export function registerScreens(store, Provider) { - Navigation.registerComponent('About', () => wrapWithContextProvider(require('app/screens/about').default), store, Provider); - Navigation.registerComponent('AddReaction', () => wrapWithContextProvider(require('app/screens/add_reaction').default), store, Provider); - Navigation.registerComponent('AdvancedSettings', () => wrapWithContextProvider(require('app/screens/settings/advanced_settings').default), store, Provider); - Navigation.registerComponent('Channel', () => wrapWithContextProvider(Channel, false), store, Provider); - Navigation.registerComponent('ChannelAddMembers', () => wrapWithContextProvider(require('app/screens/channel_add_members').default), store, Provider); - Navigation.registerComponent('ChannelInfo', () => wrapWithContextProvider(require('app/screens/channel_info').default), store, Provider); - Navigation.registerComponent('ChannelMembers', () => wrapWithContextProvider(require('app/screens/channel_members').default), store, Provider); - Navigation.registerComponent('ChannelPeek', () => wrapWithContextProvider(require('app/screens/channel_peek').default), store, Provider); - Navigation.registerComponent('ClientUpgrade', () => wrapWithContextProvider(require('app/screens/client_upgrade').default), store, Provider); - Navigation.registerComponent('ClockDisplay', () => wrapWithContextProvider(require('app/screens/clock_display').default), store, Provider); - Navigation.registerComponent('Code', () => wrapWithContextProvider(require('app/screens/code').default), store, Provider); - Navigation.registerComponent('CreateChannel', () => wrapWithContextProvider(require('app/screens/create_channel').default), store, Provider); - Navigation.registerComponent('DisplaySettings', () => wrapWithContextProvider(require('app/screens/settings/display_settings').default), store, Provider); - Navigation.registerComponent('EditChannel', () => wrapWithContextProvider(require('app/screens/edit_channel').default), store, Provider); - Navigation.registerComponent('EditPost', () => wrapWithContextProvider(require('app/screens/edit_post').default), store, Provider); - Navigation.registerComponent('EditProfile', () => wrapWithContextProvider(require('app/screens/edit_profile').default), store, Provider); - Navigation.registerComponent('Entry', () => Entry, store, Provider); - Navigation.registerComponent('ExpandedAnnouncementBanner', () => wrapWithContextProvider(require('app/screens/expanded_announcement_banner').default), store, Provider); - Navigation.registerComponent('FlaggedPosts', () => wrapWithContextProvider(require('app/screens/flagged_posts').default), store, Provider); - Navigation.registerComponent('ForgotPassword', () => wrapWithContextProvider(require('app/screens/forgot_password').default), store, Provider); - Navigation.registerComponent('ImagePreview', () => wrapWithContextProvider(require('app/screens/image_preview').default), store, Provider); - Navigation.registerComponent('InteractiveDialog', () => wrapWithContextProvider(require('app/screens/interactive_dialog').default), store, Provider); - Navigation.registerComponent('Login', () => wrapWithContextProvider(require('app/screens/login').default), store, Provider); - Navigation.registerComponent('LoginOptions', () => wrapWithContextProvider(require('app/screens/login_options').default), store, Provider); - Navigation.registerComponent('LongPost', () => wrapWithContextProvider(require('app/screens/long_post').default), store, Provider); - Navigation.registerComponent('MFA', () => wrapWithContextProvider(require('app/screens/mfa').default), store, Provider); - Navigation.registerComponent('MoreChannels', () => wrapWithContextProvider(require('app/screens/more_channels').default), store, Provider); - Navigation.registerComponent('MoreDirectMessages', () => wrapWithContextProvider(require('app/screens/more_dms').default), store, Provider); - Navigation.registerComponent('Notification', () => wrapWithContextProvider(require('app/screens/notification').default), store, Provider); - Navigation.registerComponent('NotificationSettings', () => wrapWithContextProvider(require('app/screens/settings/notification_settings').default), store, Provider); - Navigation.registerComponent('NotificationSettingsAutoResponder', () => wrapWithContextProvider(require('app/screens/settings/notification_settings_auto_responder').default), store, Provider); - Navigation.registerComponent('NotificationSettingsEmail', () => wrapWithContextProvider(require('app/screens/settings/notification_settings_email').default), store, Provider); - Navigation.registerComponent('NotificationSettingsMentions', () => wrapWithContextProvider(require('app/screens/settings/notification_settings_mentions').default), store, Provider); - Navigation.registerComponent('NotificationSettingsMentionsKeywords', () => wrapWithContextProvider(require('app/screens/settings/notification_settings_mentions_keywords').default), store, Provider); - Navigation.registerComponent('NotificationSettingsMobile', () => wrapWithContextProvider(require('app/screens/settings/notification_settings_mobile').default), store, Provider); - Navigation.registerComponent('OptionsModal', () => wrapWithContextProvider(require('app/screens/options_modal').default), store, Provider); - Navigation.registerComponent('Permalink', () => wrapWithContextProvider(require('app/screens/permalink').default), store, Provider); - Navigation.registerComponent('PinnedPosts', () => wrapWithContextProvider(require('app/screens/pinned_posts').default), store, Provider); - Navigation.registerComponent('PostOptions', () => gestureHandlerRootHOC(wrapWithContextProvider(require('app/screens/post_options').default)), store, Provider); - Navigation.registerComponent('ReactionList', () => gestureHandlerRootHOC(wrapWithContextProvider(require('app/screens/reaction_list').default)), store, Provider); - Navigation.registerComponent('RecentMentions', () => wrapWithContextProvider(require('app/screens/recent_mentions').default), store, Provider); - Navigation.registerComponent('Root', () => require('app/screens/root').default, store, Provider); - Navigation.registerComponent('Search', () => wrapWithContextProvider(require('app/screens/search').default), store, Provider); - Navigation.registerComponent('SelectorScreen', () => wrapWithContextProvider(require('app/screens/selector_screen').default), store, Provider); - Navigation.registerComponent('SelectServer', () => wrapWithContextProvider(SelectServer), store, Provider); - Navigation.registerComponent('SelectTeam', () => wrapWithContextProvider(require('app/screens/select_team').default), store, Provider); - Navigation.registerComponent('SelectTimezone', () => wrapWithContextProvider(require('app/screens/timezone/select_timezone').default), store, Provider); - Navigation.registerComponent('Settings', () => wrapWithContextProvider(require('app/screens/settings/general').default), store, Provider); - Navigation.registerComponent('SSO', () => wrapWithContextProvider(require('app/screens/sso').default), store, Provider); - Navigation.registerComponent('Table', () => wrapWithContextProvider(require('app/screens/table').default), store, Provider); - Navigation.registerComponent('TableImage', () => wrapWithContextProvider(require('app/screens/table_image').default), store, Provider); - Navigation.registerComponent('TermsOfService', () => wrapWithContextProvider(require('app/screens/terms_of_service').default), store, Provider); - Navigation.registerComponent('TextPreview', () => wrapWithContextProvider(require('app/screens/text_preview').default), store, Provider); - Navigation.registerComponent('ThemeSettings', () => wrapWithContextProvider(require('app/screens/theme').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); + // TODO consolidate this with app/utils/wrap_context_provider + const wrapper = (Comp) => (props) => ( // eslint-disable-line react/display-name + + + + + + ); + + Navigation.registerComponent('About', () => wrapper(require('app/screens/about').default), () => require('app/screens/about').default); + Navigation.registerComponent('AddReaction', () => wrapper(require('app/screens/add_reaction').default), () => require('app/screens/add_reaction').default); + Navigation.registerComponent('AdvancedSettings', () => wrapper(require('app/screens/settings/advanced_settings').default), () => require('app/screens/settings/advanced_settings').default); + Navigation.registerComponent('Channel', () => wrapper(Channel), () => Channel); + Navigation.registerComponent('ChannelAddMembers', () => wrapper(require('app/screens/channel_add_members').default), () => require('app/screens/channel_add_members').default); + Navigation.registerComponent('ChannelInfo', () => wrapper(require('app/screens/channel_info').default), () => require('app/screens/channel_info').default); + Navigation.registerComponent('ChannelMembers', () => wrapper(require('app/screens/channel_members').default), () => require('app/screens/channel_members').default); + Navigation.registerComponent('ChannelPeek', () => wrapper(require('app/screens/channel_peek').default), () => require('app/screens/channel_peek').default); + Navigation.registerComponent('ClientUpgrade', () => wrapper(require('app/screens/client_upgrade').default), () => require('app/screens/client_upgrade').default); + Navigation.registerComponent('ClockDisplay', () => wrapper(require('app/screens/clock_display').default), () => require('app/screens/clock_display').default); + Navigation.registerComponent('Code', () => wrapper(require('app/screens/code').default), () => require('app/screens/code').default); + Navigation.registerComponent('CreateChannel', () => wrapper(require('app/screens/create_channel').default), () => require('app/screens/create_channel').default); + Navigation.registerComponent('DisplaySettings', () => wrapper(require('app/screens/settings/display_settings').default), () => require('app/screens/settings/display_settings').default); + Navigation.registerComponent('EditChannel', () => wrapper(require('app/screens/edit_channel').default), () => require('app/screens/edit_channel').default); + Navigation.registerComponent('EditPost', () => wrapper(require('app/screens/edit_post').default), () => require('app/screens/edit_post').default); + Navigation.registerComponent('EditProfile', () => wrapper(require('app/screens/edit_profile').default), () => require('app/screens/edit_profile').default); + Navigation.registerComponent('Entry', () => wrapper(Entry), () => Entry); + Navigation.registerComponent('ExpandedAnnouncementBanner', () => wrapper(require('app/screens/expanded_announcement_banner').default), () => require('app/screens/expanded_announcement_banner').default); + Navigation.registerComponent('FlaggedPosts', () => wrapper(require('app/screens/flagged_posts').default), () => require('app/screens/flagged_posts').default); + Navigation.registerComponent('ForgotPassword', () => wrapper(require('app/screens/forgot_password').default), () => require('app/screens/forgot_password').default); + Navigation.registerComponent('ImagePreview', () => wrapper(require('app/screens/image_preview').default), () => require('app/screens/image_preview').default); + Navigation.registerComponent('InteractiveDialog', () => wrapper(require('app/screens/interactive_dialog').default), () => require('app/screens/interactive_dialog').default); + Navigation.registerComponent('Login', () => wrapper(require('app/screens/login').default), () => require('app/screens/login').default); + Navigation.registerComponent('LoginOptions', () => wrapper(require('app/screens/login_options').default), () => require('app/screens/login_options').default); + Navigation.registerComponent('LongPost', () => wrapper(require('app/screens/long_post').default), () => require('app/screens/long_post').default); + Navigation.registerComponent('MFA', () => wrapper(require('app/screens/mfa').default), () => require('app/screens/mfa').default); + Navigation.registerComponent('MoreChannels', () => wrapper(require('app/screens/more_channels').default), () => require('app/screens/more_channels').default); + Navigation.registerComponent('MoreDirectMessages', () => wrapper(require('app/screens/more_dms').default), () => require('app/screens/more_dms').default); + Navigation.registerComponent('Notification', () => wrapper(require('app/screens/notification').default), () => require('app/screens/notification').default); + Navigation.registerComponent('NotificationSettings', () => wrapper(require('app/screens/settings/notification_settings').default), () => require('app/screens/settings/notification_settings').default); + Navigation.registerComponent('NotificationSettingsAutoResponder', () => wrapper(require('app/screens/settings/notification_settings_auto_responder').default), () => require('app/screens/settings/notification_settings_auto_responder').default); + Navigation.registerComponent('NotificationSettingsEmail', () => wrapper(require('app/screens/settings/notification_settings_email').default), () => require('app/screens/settings/notification_settings_email').default); + Navigation.registerComponent('NotificationSettingsMentions', () => wrapper(require('app/screens/settings/notification_settings_mentions').default), () => require('app/screens/settings/notification_settings_mentions').default); + Navigation.registerComponent('NotificationSettingsMentionsKeywords', () => wrapper(require('app/screens/settings/notification_settings_mentions_keywords').default), () => require('app/screens/settings/notification_settings_mentions_keywords').default); + Navigation.registerComponent('NotificationSettingsMobile', () => wrapper(require('app/screens/settings/notification_settings_mobile').default), () => require('app/screens/settings/notification_settings_mobile').default); + Navigation.registerComponent('OptionsModal', () => wrapper(require('app/screens/options_modal').default), () => require('app/screens/options_modal').default); + Navigation.registerComponent('Permalink', () => wrapper(require('app/screens/permalink').default), () => require('app/screens/permalink').default); + Navigation.registerComponent('PinnedPosts', () => wrapper(require('app/screens/pinned_posts').default), () => require('app/screens/pinned_posts').default); + Navigation.registerComponent('PostOptions', () => gestureHandlerRootHOC(wrapper(require('app/screens/post_options').default)), () => require('app/screens/post_options').default); + Navigation.registerComponent('ReactionList', () => gestureHandlerRootHOC(wrapper(require('app/screens/reaction_list').default)), () => require('app/screens/reaction_list').default); + Navigation.registerComponent('RecentMentions', () => wrapper(require('app/screens/recent_mentions').default), () => require('app/screens/recent_mentions').default); + Navigation.registerComponent('Root', () => wrapper(Root), () => Root); + Navigation.registerComponent('Search', () => wrapper(require('app/screens/search').default), () => require('app/screens/search').default); + Navigation.registerComponent('SelectorScreen', () => wrapper(require('app/screens/selector_screen').default), () => require('app/screens/selector_screen').default); + Navigation.registerComponent('SelectServer', () => wrapper(SelectServer), () => SelectServer); + Navigation.registerComponent('SelectTeam', () => wrapper(require('app/screens/select_team').default), () => require('app/screens/select_team').default); + Navigation.registerComponent('SelectTimezone', () => wrapper(require('app/screens/timezone/select_timezone').default), () => require('app/screens/timezone/select_timezone').default); + Navigation.registerComponent('Settings', () => wrapper(require('app/screens/settings/general').default), () => require('app/screens/settings/general').default); + Navigation.registerComponent('SSO', () => wrapper(require('app/screens/sso').default), () => require('app/screens/sso').default); + Navigation.registerComponent('Table', () => wrapper(require('app/screens/table').default), () => require('app/screens/table').default); + Navigation.registerComponent('TableImage', () => wrapper(require('app/screens/table_image').default), () => require('app/screens/table_image').default); + Navigation.registerComponent('TermsOfService', () => wrapper(require('app/screens/terms_of_service').default), () => require('app/screens/terms_of_service').default); + Navigation.registerComponent('TextPreview', () => wrapper(require('app/screens/text_preview').default), () => require('app/screens/text_preview').default); + Navigation.registerComponent('ThemeSettings', () => wrapper(require('app/screens/theme').default), () => require('app/screens/theme').default); + Navigation.registerComponent('Thread', () => wrapper(require('app/screens/thread').default), () => require('app/screens/thread').default); + Navigation.registerComponent('TimezoneSettings', () => wrapper(require('app/screens/timezone').default), () => require('app/screens/timezone').default); + Navigation.registerComponent('ErrorTeamsList', () => wrapper(require('app/screens/error_teams_list').default), () => require('app/screens/error_teams_list').default); + Navigation.registerComponent('UserProfile', () => wrapper(require('app/screens/user_profile').default), () => require('app/screens/user_profile').default); } diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 09998aaba..3832c6925 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -92,7 +92,6 @@ 7FABE04622137F5C00D0F595 /* libUploadAttachments.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FABE04522137F2A00D0F595 /* libUploadAttachments.a */; }; 7FABE0562213884700D0F595 /* libUploadAttachments.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FABE04522137F2A00D0F595 /* libUploadAttachments.a */; }; 7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; }; - 7FC200E81EBB65370099331B /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */; }; 7FDB92B11F706F58006CDFD1 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */; }; 7FEB10981F6101710039A015 /* BlurAppScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB10971F6101710039A015 /* BlurAppScreen.m */; }; 7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; }; @@ -101,6 +100,7 @@ 7FF31C1421330B7900680B75 /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FF31C1321330B4200680B75 /* libRNFetchBlob.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 84E3264B229834C30055068A /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E325FF229834C30055068A /* Config.swift */; }; + 84596ECE229D93FD00981086 /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84596EB1229D93B600981086 /* libReactNativeNavigation.a */; }; 895C9A56B94A45C1BAF568FE /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */; }; 8D26455C994F46C39B1392F2 /* libRNSafeArea.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9263CF9B16054263B13EA23B /* libRNSafeArea.a */; }; 9358B95F95184EE0A4DCE629 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4B1B363C2414DA19C1AC521 /* OpenSans-Bold.ttf */; }; @@ -628,13 +628,6 @@ remoteGlobalIDString = 7FABE03622137F2900D0F595; remoteInfo = UploadAttachments; }; - 7FC200DE1EBB65100099331B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D; - remoteInfo = ReactNativeNavigation; - }; 7FDB92A61F706F45006CDFD1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */; @@ -698,6 +691,20 @@ remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; + 84596EB0229D93B600981086 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84596E7B229D93B600981086 /* ReactNativeNavigation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D; + remoteInfo = ReactNativeNavigation; + }; + 84596EB2229D93B600981086 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84596E7B229D93B600981086 /* ReactNativeNavigation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7B49FEBB1E95090800DEB3EA; + remoteInfo = ReactNativeNavigationTests; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -831,7 +838,6 @@ 7FABDFC12211A39000D0F595 /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; 7FABE0092212650600D0F595 /* ChannelsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsViewController.swift; sourceTree = ""; }; 7FABE04022137F2900D0F595 /* UploadAttachments.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = UploadAttachments.xcodeproj; path = UploadAttachments/UploadAttachments.xcodeproj; sourceTree = ""; }; - 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = ""; }; 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = ""; }; 7FEB10961F6101710039A015 /* BlurAppScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlurAppScreen.h; path = Mattermost/BlurAppScreen.h; sourceTree = ""; }; 7FEB10971F6101710039A015 /* BlurAppScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlurAppScreen.m; path = Mattermost/BlurAppScreen.m; sourceTree = ""; }; @@ -853,6 +859,8 @@ 7FFE32BE1FD9CCAA0038C7A0 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7FFE32BF1FD9CCAA0038C7A0 /* Sentry.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Sentry.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; + 84596DD7229C853000981086 /* libReactNativeNavigation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReactNativeNavigation.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 84596E7B229D93B600981086 /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj"; sourceTree = ""; }; 849D881A0372465294DE7315 /* RNSafeArea.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSafeArea.xcodeproj; path = "../node_modules/react-native-safe-area/ios/RNSafeArea.xcodeproj"; sourceTree = ""; }; 84E325FF229834C30055068A /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 8F0B22D2C9924FAFA7FB681C /* Roboto-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-LightItalic.ttf"; path = "../assets/fonts/Roboto-LightItalic.ttf"; sourceTree = ""; }; @@ -920,7 +928,7 @@ 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 7F43D63F1F6BFA19001FC614 /* libBVLinearGradient.a in Frameworks */, - 7FC200E81EBB65370099331B /* libReactNativeNavigation.a in Frameworks */, + 84596ECE229D93FD00981086 /* libReactNativeNavigation.a in Frameworks */, 7F1A56B4227E38B600EF7A90 /* libRNCAsyncStorage.a in Frameworks */, 7F4C2598227E3B11009144EF /* libRNCNetInfo.a in Frameworks */, 7F2691A11EE1DC6A007574FE /* libRNNotifications.a in Frameworks */, @@ -1212,6 +1220,7 @@ 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */, 4246DC09024BB33A3E491E25 /* libPods-MattermostTests.a */, CF19152887874B7E996210B1 /* libz.tbd */, + 84596DD7229C853000981086 /* libReactNativeNavigation.a */, ); name = Frameworks; sourceTree = ""; @@ -1444,14 +1453,6 @@ name = Products; sourceTree = ""; }; - 7FC200BD1EBB65100099331B /* Products */ = { - isa = PBXGroup; - children = ( - 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */, - ); - name = Products; - sourceTree = ""; - }; 7FDB92761F706F45006CDFD1 /* Products */ = { isa = PBXGroup; children = ( @@ -1505,6 +1506,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 84596E7B229D93B600981086 /* ReactNativeNavigation.xcodeproj */, 7FABE04022137F2900D0F595 /* UploadAttachments.xcodeproj */, 7F11AA06228848D8001C9540 /* KeyboardTrackingView.xcodeproj */, 7F1A5663227E389600EF7A90 /* RNCAsyncStorage.xcodeproj */, @@ -1520,7 +1522,6 @@ 37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */, 3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */, 7F26919B1EE1DC51007574FE /* RNNotifications.xcodeproj */, - 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */, 7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */, 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, @@ -1589,6 +1590,15 @@ name = Products; sourceTree = ""; }; + 84596E7C229D93B600981086 /* Products */ = { + isa = PBXGroup; + children = ( + 84596EB1229D93B600981086 /* libReactNativeNavigation.a */, + 84596EB3229D93B600981086 /* ReactNativeNavigationTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1824,8 +1834,8 @@ ProjectRef = 3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */; }, { - ProductGroup = 7FC200BD1EBB65100099331B /* Products */; - ProjectRef = 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */; + ProductGroup = 84596E7C229D93B600981086 /* Products */; + ProjectRef = 84596E7B229D93B600981086 /* ReactNativeNavigation.xcodeproj */; }, { ProductGroup = 7F7D7F53201645D300D31155 /* Products */; @@ -2388,13 +2398,6 @@ remoteRef = 7FABE04422137F2A00D0F595 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReactNativeNavigation.a; - remoteRef = 7FC200DE1EBB65100099331B /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -2451,6 +2454,20 @@ remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 84596EB1229D93B600981086 /* libReactNativeNavigation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReactNativeNavigation.a; + remoteRef = 84596EB0229D93B600981086 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 84596EB3229D93B600981086 /* ReactNativeNavigationTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = ReactNativeNavigationTests.xctest; + remoteRef = 84596EB2229D93B600981086 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -2768,7 +2785,7 @@ ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../node_modules/react-native/Libraries/PushNotificationIOS/**", - "$(SRCROOT)/../node_modules/react-native-navigation/ios/**", + "$(SRCROOT)/../node_modules/react-native-navigation/lib/ios/**", "$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**", "$(SRCROOT)/../node_modules/react-native-local-auth", "$(SRCROOT)/../node_modules/react-native-passcode-status/ios", @@ -2828,7 +2845,7 @@ ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../node_modules/react-native/Libraries/PushNotificationIOS/**", - "$(SRCROOT)/../node_modules/react-native-navigation/ios/**", + "$(SRCROOT)/../node_modules/react-native-navigation/lib/ios/**", "$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**", "$(SRCROOT)/../node_modules/react-native-local-auth", "$(SRCROOT)/../node_modules/react-native-passcode-status/ios", diff --git a/ios/Mattermost/AppDelegate.m b/ios/Mattermost/AppDelegate.m index 1e3ff9097..f261b1423 100644 --- a/ios/Mattermost/AppDelegate.m +++ b/ios/Mattermost/AppDelegate.m @@ -17,7 +17,7 @@ #else #import "RNSentry.h" // This is used for versions of react < 0.40 #endif -#import "RCCManager.h" +#import #import "RNNotifications.h" #import #import @@ -53,13 +53,11 @@ NSString* const NotificationClearAction = @"clear"; [[NSUserDefaults standardUserDefaults] synchronize]; } - NSURL *jsCodeLocation; - - jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor]; - [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil]; os_log(OS_LOG_DEFAULT, "Mattermost started!!"); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index de555aa2b..0830f40fa 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 63cb0d0aef536c8cbf15bf664fcfe31852a13dd1 -COCOAPODS: 1.5.3 +COCOAPODS: 1.6.1 diff --git a/package-lock.json b/package-lock.json index 6c9b3a126..74ecbe0bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7685,25 +7685,25 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { @@ -7713,13 +7713,13 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "optional": true, "requires": { @@ -7729,37 +7729,37 @@ }, "chownr": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "optional": true, "requires": { @@ -7768,25 +7768,25 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "optional": true, "requires": { @@ -7795,13 +7795,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { @@ -7817,7 +7817,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "optional": true, "requires": { @@ -7831,13 +7831,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "optional": true, "requires": { @@ -7846,7 +7846,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "optional": true, "requires": { @@ -7855,7 +7855,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { @@ -7865,19 +7865,19 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "optional": true, "requires": { @@ -7886,13 +7886,13 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "optional": true, "requires": { @@ -7901,13 +7901,13 @@ }, "minimist": { "version": "0.0.8", - "resolved": "", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "optional": true }, "minipass": { "version": "2.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "optional": true, "requires": { @@ -7917,7 +7917,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "optional": true, "requires": { @@ -7926,7 +7926,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "optional": true, "requires": { @@ -7935,13 +7935,13 @@ }, "ms": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "optional": true }, "needle": { "version": "2.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "optional": true, "requires": { @@ -7952,7 +7952,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "optional": true, "requires": { @@ -7970,7 +7970,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "optional": true, "requires": { @@ -7980,13 +7980,13 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "optional": true, "requires": { @@ -7996,7 +7996,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { @@ -8008,19 +8008,19 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "optional": true, "requires": { @@ -8029,19 +8029,19 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { @@ -8051,19 +8051,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { @@ -8075,7 +8075,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "optional": true } @@ -8083,7 +8083,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "optional": true, "requires": { @@ -8098,7 +8098,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "optional": true, "requires": { @@ -8107,43 +8107,43 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.7.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "optional": true, "requires": { @@ -8154,7 +8154,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { @@ -8163,7 +8163,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "optional": true, "requires": { @@ -8172,13 +8172,13 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "optional": true, "requires": { @@ -8193,13 +8193,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { @@ -8208,13 +8208,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true }, "yallist": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "optional": true } @@ -14201,7 +14201,7 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, @@ -14213,7 +14213,7 @@ }, "cliui": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { @@ -14242,7 +14242,7 @@ }, "find-up": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { @@ -14257,13 +14257,13 @@ }, "invert-kv": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "lcid": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { @@ -14272,7 +14272,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { @@ -14299,7 +14299,7 @@ }, "os-locale": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { @@ -14319,7 +14319,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { @@ -14340,7 +14340,7 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, @@ -14374,7 +14374,7 @@ }, "strip-ansi": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { @@ -14383,13 +14383,13 @@ }, "uuid": { "version": "3.3.2", - "resolved": "", + "resolved": false, "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "y18n": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, @@ -15570,10 +15570,30 @@ "from": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab" }, "react-native-navigation": { - "version": "github:migbot/react-native-navigation#03c623c373f818827a463ca0fe90f86f56e71b2f", - "from": "github:migbot/react-native-navigation#03c623c373f818827a463ca0fe90f86f56e71b2f", + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-2.20.2.tgz", + "integrity": "sha512-Ok1x2bBFLcIGzYF5k9SFikkUSDr4MWp1zLYy6KernL4g20DE9flgPphOrllbOEDSA87NwDUv+QsUPbpOPeW1iw==", "requires": { - "lodash": "4.x.x" + "hoist-non-react-statics": "3.x.x", + "lodash": "4.17.x", + "prop-types": "15.x.x", + "react-lifecycles-compat": "2.0.0", + "tslib": "1.9.3" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "react-lifecycles-compat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-2.0.0.tgz", + "integrity": "sha512-txfpPCQYiazVdcbMRhatqWKcAxJweUu2wDXvts5/7Wyp6+Y9cHojqXHsLPEckzutfHlxZhG8Oiundbmp8Fd6eQ==" + } } }, "react-native-notifications": { diff --git a/package.json b/package.json index f15232eba..11abbc33b 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "react-native-keychain": "3.1.3", "react-native-linear-gradient": "2.5.4", "react-native-local-auth": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab", - "react-native-navigation": "github:migbot/react-native-navigation#03c623c373f818827a463ca0fe90f86f56e71b2f", + "react-native-navigation": "2.20.2", "react-native-notifications": "github:mattermost/react-native-notifications#721fcb41b3c265a5eec208a1df181d4ffcafa477", "react-native-passcode-status": "1.1.1", "react-native-permissions": "1.1.1", From 8d2b2b09d92b749d22fd741572f2c4ac83871856 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Wed, 5 Jun 2019 11:25:54 -0400 Subject: [PATCH 02/19] Fix incorrect merge --- package-lock.json | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5a97e4f6..622671a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7669,12 +7669,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true -======= - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" ->>>>>>> master }, "aproba": { "version": "1.2.0", @@ -7695,12 +7691,8 @@ "balanced-match": { "version": "1.0.0", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true -======= - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" ->>>>>>> master }, "brace-expansion": { "version": "1.1.11", @@ -7720,32 +7712,20 @@ "code-point-at": { "version": "1.1.0", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true -======= - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" ->>>>>>> master }, "concat-map": { "version": "0.0.1", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true -======= - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" ->>>>>>> master }, "console-control-strings": { "version": "1.1.0", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true -======= - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" ->>>>>>> master }, "core-util-is": { "version": "1.0.2", @@ -7862,12 +7842,8 @@ "inherits": { "version": "2.0.3", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "optional": true -======= - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" ->>>>>>> master }, "ini": { "version": "1.3.5", @@ -7900,12 +7876,8 @@ "minimist": { "version": "0.0.8", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "optional": true -======= - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" ->>>>>>> master }, "minipass": { "version": "2.3.5", @@ -8009,12 +7981,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true -======= - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" ->>>>>>> master }, "object-assign": { "version": "4.1.1", @@ -8111,12 +8079,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": false, -<<<<<<< HEAD "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true -======= - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" ->>>>>>> master }, "safer-buffer": { "version": "2.1.2", @@ -8214,22 +8178,14 @@ "wrappy": { "version": "1.0.2", "resolved": false, -<<<<<<< HEAD "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true -======= - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" ->>>>>>> master }, "yallist": { "version": "3.0.3", "resolved": false, -<<<<<<< HEAD "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "optional": true -======= - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" ->>>>>>> master } } }, From 73792d5526d8881e5304f8a14830ab0afa885b6f Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 11 Jun 2019 18:10:14 -0400 Subject: [PATCH 03/19] MM-16011 Update navigation through Login and Channel screens (#2870) * Update navigation through Login and Channel screens * Stop using static options and mergeOptions * Update unit tests --- app/actions/navigation.js | 78 +++++++++++++++++++++ app/app.js | 11 +-- app/constants/view.js | 3 - app/mattermost.js | 63 ++--------------- app/screens/entry/entry.js | 68 ------------------ app/screens/index.js | 1 + app/screens/login/index.js | 6 +- app/screens/login/login.js | 22 ++---- app/screens/login/login.test.js | 42 ++++++++++++ app/screens/login_options/login_options.js | 58 ++++++++++------ app/screens/select_server/index.js | 6 +- app/screens/select_server/select_server.js | 80 +++++++++++----------- 12 files changed, 221 insertions(+), 217 deletions(-) create mode 100644 app/actions/navigation.js diff --git a/app/actions/navigation.js b/app/actions/navigation.js new file mode 100644 index 000000000..b206b1e3b --- /dev/null +++ b/app/actions/navigation.js @@ -0,0 +1,78 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Navigation} from 'react-native-navigation'; + +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + +export function resetToChannel() { + return (dispatch, getState) => { + const theme = getTheme(getState()); + + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name: 'Channel', + options: { + topBar: { + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + statusBar: { + visible: true, + }, + title: { + color: theme.sidebarHeaderTextColor, + }, + visible: false, + }, + }, + }, + }], + }, + }, + }); + }; +} + +export function resetToSelectServer(allowOtherServers) { + return (dispatch, getState) => { + const theme = getTheme(getState()); + + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name: 'SelectServer', + passProps: { + allowOtherServers, + }, + options: { + topBar: { + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + statusBar: { + visible: true, + }, + visible: false, + }, + }, + }, + }], + }, + }, + }); + }; +} diff --git a/app/app.js b/app/app.js index 36846618d..36baf2a1a 100644 --- a/app/app.js +++ b/app/app.js @@ -8,15 +8,14 @@ import {setGenericPassword, getGenericPassword, resetGenericPassword} from 'reac import {loadMe} from 'mattermost-redux/actions/users'; import {Client4} from 'mattermost-redux/client'; -import EventEmitter from 'mattermost-redux/utils/event_emitter'; +import {resetToChannel, resetToSelectServer} from 'app/actions/navigation'; import {setDeepLinkURL} from 'app/actions/views/root'; -import {ViewTypes} from 'app/constants'; import tracker from 'app/utils/time_tracker'; import {getCurrentLocale} from 'app/selectors/i18n'; import {getTranslations as getLocalTranslations} from 'app/i18n'; -import {store, handleManagedConfig} from 'app/mattermost'; +import {store, handleManagedConfig, initializeModules} from 'app/mattermost'; import avoidNativeBridge from 'app/utils/avoid_native_bridge'; import {setCSRFFromCookie} from 'app/utils/security'; @@ -302,13 +301,15 @@ export default class App { switch (screen) { case 'SelectServer': - EventEmitter.emit(ViewTypes.LAUNCH_LOGIN, true); + dispatch(resetToSelectServer(this.allowOtherServers)); break; case 'Channel': - EventEmitter.emit(ViewTypes.LAUNCH_CHANNEL, true); + dispatch(resetToChannel()); break; } + initializeModules(); + this.setAppStarted(true); } } diff --git a/app/constants/view.js b/app/constants/view.js index b885a87e9..32f89bbf1 100644 --- a/app/constants/view.js +++ b/app/constants/view.js @@ -80,9 +80,6 @@ const ViewTypes = keyMirror({ INCREMENT_EMOJI_PICKER_PAGE: null, - LAUNCH_LOGIN: null, - LAUNCH_CHANNEL: null, - SET_DEEP_LINK_URL: null, SET_PROFILE_IMAGE_URI: null, diff --git a/app/mattermost.js b/app/mattermost.js index 9b740bc5e..a92049115 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -26,6 +26,7 @@ import {General} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {resetToChannel, resetToSelectServer} from 'app/actions/navigation'; import {selectDefaultChannel} from 'app/actions/views/channel'; import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from 'app/actions/device'; import {handleLoginIdChanged} from 'app/actions/views/login'; @@ -75,7 +76,7 @@ const lazyLoadAnalytics = () => { }; }; -const initializeModules = () => { +export const initializeModules = () => { const { StatusBarSizeIOS, initializeErrorHandling, @@ -141,7 +142,7 @@ const handleLogout = () => { app.clearNativeCache(); deleteFileCache(); resetBadgeAndVersion(); - launchSelectServer(); + store.dispatch(resetToSelectServer(app.allowOtherServers)); }; const restartApp = async () => { @@ -165,7 +166,7 @@ const restartApp = async () => { console.warn('Failed to load initial data while restarting', e); // eslint-disable-line no-console } - launchChannel(); + store.dispatch(resetToChannel()); }; const handleServerVersionChanged = async (serverVersion) => { @@ -384,59 +385,6 @@ const handleSwitchToDefaultChannel = (teamId) => { store.dispatch(selectDefaultChannel(teamId)); }; -const launchSelectServer = () => { - Navigation.setRoot({ - root: { - stack: { - children: [{ - component: { - name: 'SelectServer', - passProps: { - allowOtherServers: app.allowOtherServers, - }, - }, - }], - options: { - layout: { - backgroundColor: 'transparent', - }, - statusBar: { - visible: true, - }, - topBar: { - visible: false, - }, - }, - }, - }, - }); -}; - -const launchChannel = () => { - Navigation.setRoot({ - root: { - stack: { - children: [{ - component: { - name: 'Channel', - }, - }], - options: { - layout: { - backgroundColor: 'transparent', - }, - statusBar: { - visible: true, - }, - topBar: { - visible: false, - }, - }, - }, - }, - }); -}; - const handleAppStateChange = (appState) => { const isActive = appState === 'active'; @@ -506,9 +454,6 @@ const launchEntry = () => { children: [{ component: { name: 'Entry', - passProps: { - initializeModules, - }, }, }], options: { diff --git a/app/screens/entry/entry.js b/app/screens/entry/entry.js index 500023a61..c7d576639 100644 --- a/app/screens/entry/entry.js +++ b/app/screens/entry/entry.js @@ -13,16 +13,13 @@ import DeviceInfo from 'react-native-device-info'; import {setSystemEmojis} from 'mattermost-redux/actions/emojis'; import {Client4} from 'mattermost-redux/client'; -import EventEmitter from 'mattermost-redux/utils/event_emitter'; import { app, store, } from 'app/mattermost'; -import {ViewTypes} from 'app/constants'; import PushNotifications from 'app/push_notifications'; import {stripTrailingSlashes} from 'app/utils/url'; -import {wrapWithContextProvider} from 'app/utils/wrap_context_provider'; import ChannelLoader from 'app/components/channel_loader'; import EmptyToolbar from 'app/components/start/empty_toolbar'; @@ -30,14 +27,6 @@ import Loading from 'app/components/loading'; import SafeAreaView from 'app/components/safe_area_view'; import StatusBar from 'app/components/status_bar'; -const lazyLoadSelectServer = () => { - return require('app/screens/select_server').default; -}; - -const lazyLoadChannel = () => { - return require('app/screens/channel').default; -}; - const lazyLoadPushNotifications = () => { return require('app/utils/push_notifications').configurePushNotifications; }; @@ -61,7 +50,6 @@ export default class Entry extends PureComponent { isLandscape: PropTypes.bool, enableTimezone: PropTypes.bool, deviceTimezone: PropTypes.string, - initializeModules: PropTypes.func, actions: PropTypes.shape({ autoUpdateTimezone: PropTypes.func.isRequired, setDeviceToken: PropTypes.func.isRequired, @@ -71,11 +59,6 @@ export default class Entry extends PureComponent { constructor(props) { super(props); - this.state = { - launchLogin: false, - launchChannel: false, - }; - this.unsubscribeFromStore = null; } @@ -87,32 +70,8 @@ export default class Entry extends PureComponent { } else { this.unsubscribeFromStore = store.subscribe(this.listenForHydration); } - - EventEmitter.on(ViewTypes.LAUNCH_LOGIN, this.handleLaunchLogin); - EventEmitter.on(ViewTypes.LAUNCH_CHANNEL, this.handleLaunchChannel); } - componentWillUnmount() { - EventEmitter.off(ViewTypes.LAUNCH_LOGIN, this.handleLaunchLogin); - EventEmitter.off(ViewTypes.LAUNCH_CHANNEL, this.handleLaunchChannel); - } - - handleLaunchLogin = (initializeModules) => { - this.setState({launchLogin: true}); - - if (initializeModules) { - this.props.initializeModules(); - } - }; - - handleLaunchChannel = (initializeModules) => { - this.setState({launchChannel: true}); - - if (initializeModules) { - this.props.initializeModules(); - } - }; - listenForHydration = () => { const {getState} = store; const state = getState(); @@ -245,39 +204,12 @@ export default class Entry extends PureComponent { setSystemEmojis(EmojiIndicesByAlias); }; - renderLogin = () => { - const SelectServer = lazyLoadSelectServer(); - const props = { - allowOtherServers: app.allowOtherServers, - navigator: this.props.navigator, - }; - - return wrapWithContextProvider(SelectServer)(props); - }; - - renderChannel = () => { - const ChannelScreen = lazyLoadChannel(); - const props = { - navigator: this.props.navigator, - }; - - return wrapWithContextProvider(ChannelScreen, false)(props); - }; - render() { const { navigator, isLandscape, } = this.props; - if (this.state.launchLogin) { - return this.renderLogin(); - } - - if (this.state.launchChannel) { - return this.renderChannel(); - } - let toolbar = null; let loading = null; const backgroundColor = app.appBackground ? app.appBackground : '#ffff'; diff --git a/app/screens/index.js b/app/screens/index.js index 44865bfbe..bd19a2f6d 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -14,6 +14,7 @@ import SelectServer from 'app/screens/select_server'; const navigator = { push: () => {}, // eslint-disable-line no-empty-function setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function + setStyle: () => {}, // eslint-disable-line no-empty-function }; export function registerScreens(store, Provider) { diff --git a/app/screens/login/index.js b/app/screens/login/index.js index 3a51f5254..22e7a1956 100644 --- a/app/screens/login/index.js +++ b/app/screens/login/index.js @@ -4,11 +4,12 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import LoginActions from 'app/actions/views/login'; +import {login} from 'mattermost-redux/actions/users'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {login} from 'mattermost-redux/actions/users'; +import {resetToChannel} from 'app/actions/navigation'; +import LoginActions from 'app/actions/views/login'; import Login from './login.js'; @@ -30,6 +31,7 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ ...LoginActions, login, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/login/login.js b/app/screens/login/login.js index f05979b5d..672e1276b 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -36,15 +36,16 @@ const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa export default class Login extends PureComponent { static propTypes = { - navigator: PropTypes.object, - theme: PropTypes.object, actions: PropTypes.shape({ handleLoginIdChanged: PropTypes.func.isRequired, handlePasswordChanged: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, scheduleExpiredNotification: PropTypes.func.isRequired, login: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, + navigator: PropTypes.object.isRequired, // TODO remove me + theme: PropTypes.object, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, loginId: PropTypes.string.isRequired, @@ -66,6 +67,7 @@ export default class Login extends PureComponent { componentDidMount() { Dimensions.addEventListener('change', this.orientationDidChange); + setMfaPreflightDone(false); } @@ -84,25 +86,11 @@ export default class Login extends PureComponent { goToChannel = () => { telemetry.remove(['start:overall']); - const {navigator} = this.props; tracker.initialLoad = Date.now(); this.scheduleSessionExpiredNotification(); - navigator.resetTo({ - screen: 'Channel', - title: '', - animated: false, - backButtonTitle: '', - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - }, - }); + this.props.actions.resetToChannel(); }; goToMfa = () => { diff --git a/app/screens/login/login.test.js b/app/screens/login/login.test.js index e2ed4935a..3b345c187 100644 --- a/app/screens/login/login.test.js +++ b/app/screens/login/login.test.js @@ -3,6 +3,8 @@ import React from 'react'; +import {RequestStatus} from 'mattermost-redux/constants'; + import FormattedText from 'app/components/formatted_text'; import {shallowWithIntl} from 'test/intl-test-helper'; @@ -21,12 +23,14 @@ describe('Login', () => { loginId: '', password: '', loginRequest: {}, + navigator: {}, actions: { handleLoginIdChanged: jest.fn(), handlePasswordChanged: jest.fn(), handleSuccessfulLogin: jest.fn(), scheduleExpiredNotification: jest.fn(), login: jest.fn(), + resetToChannel: jest.fn(), }, }; @@ -72,4 +76,42 @@ describe('Login', () => { expect(wrapper.find(FormattedText).find({id: 'login.forgot'}).exists()).toBe(false); }); + + test('should send the user to the login screen after login', (done) => { + let props = { + ...baseProps, + loginRequest: { + status: RequestStatus.NOT_STARTED, + }, + }; + + props.actions.handleSuccessfulLogin.mockImplementation(() => Promise.resolve()); + props.actions.resetToChannel.mockImplementation(() => { + done(); + }); + + const wrapper = shallowWithIntl(); + + expect(props.actions.resetToChannel).not.toHaveBeenCalled(); + + props = { + ...props, + loginRequest: { + status: RequestStatus.STARTED, + }, + }; + wrapper.setProps(props); + + expect(props.actions.resetToChannel).not.toHaveBeenCalled(); + + props = { + ...props, + loginRequest: { + status: RequestStatus.SUCCESS, + }, + }; + wrapper.setProps(props); + + // This test times out if resetToChannel hasn't been called + }); }); diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js index 7ce99601c..f0b05522d 100644 --- a/app/screens/login_options/login_options.js +++ b/app/screens/login_options/login_options.js @@ -3,7 +3,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {injectIntl, intlShape} from 'react-intl'; +import {intlShape} from 'react-intl'; import { Dimensions, Image, @@ -12,6 +12,7 @@ import { Text, } from 'react-native'; import Button from 'react-native-button'; +import {Navigation} from 'react-native-navigation'; import {ViewTypes} from 'app/constants'; import FormattedText from 'app/components/formatted_text'; @@ -23,16 +24,20 @@ import LocalConfig from 'assets/config'; import gitlab from 'assets/images/gitlab.png'; import logo from 'assets/images/logo.png'; -class LoginOptions extends PureComponent { +export default class LoginOptions extends PureComponent { static propTypes = { - intl: intlShape.isRequired, - navigator: PropTypes.object, + componentId: PropTypes.string.isRequired, + navigator: PropTypes.object.isRequired, // TODO remove me config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, theme: PropTypes.object, }; - componentWillMount() { + static contextTypes = { + intl: intlShape.isRequired, + }; + + componentDidMount() { Dimensions.addEventListener('change', this.orientationDidChange); } @@ -41,23 +46,38 @@ class LoginOptions extends PureComponent { } goToLogin = preventDoubleTap(() => { - const {intl, navigator, theme} = this.props; - navigator.push({ - screen: 'Login', - title: intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, + const {intl} = this.context; + const {componentId, theme} = this.props; + + Navigation.push(componentId, { + component: { + name: 'Login', + passProps: { + theme, + }, + options: { + topBar: { + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + title: { + color: theme.sidebarHeaderTextColor, + text: intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'}), + }, + visible: true, + }, + }, }, }); }); goToSSO = (ssoType) => { - const {intl, navigator, theme} = this.props; + const {intl} = this.context; + const {navigator, theme} = this.props; navigator.push({ screen: 'SSO', title: intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'}), @@ -306,8 +326,6 @@ const style = StyleSheet.create({ flexDirection: 'column', justifyContent: 'center', paddingHorizontal: 15, - paddingVertical: 50, + flex: 1, }, }); - -export default injectIntl(LoginOptions); diff --git a/app/screens/select_server/index.js b/app/screens/select_server/index.js index 99ef53694..5a7c593e8 100644 --- a/app/screens/select_server/index.js +++ b/app/screens/select_server/index.js @@ -6,14 +6,15 @@ import {connect} from 'react-redux'; import {getPing, resetPing, setServerVersion} from 'mattermost-redux/actions/general'; import {login} from 'mattermost-redux/actions/users'; +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; +import {resetToChannel} from 'app/actions/navigation'; import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade'; import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {loadConfigAndLicense} from 'app/actions/views/root'; import {handleServerUrlChanged} from 'app/actions/views/select_server'; import getClientUpgrade from 'app/selectors/client_upgrade'; -import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; -import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import SelectServer from './select_server'; @@ -46,6 +47,7 @@ function mapDispatchToProps(dispatch) { resetPing, setLastUpgradeCheck, setServerVersion, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index e10e1b031..7d77448dd 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; +import {Navigation} from 'react-native-navigation'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import { @@ -48,17 +49,19 @@ export default class SelectServer extends PureComponent { loadConfigAndLicense: PropTypes.func.isRequired, login: PropTypes.func.isRequired, resetPing: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, setLastUpgradeCheck: PropTypes.func.isRequired, setServerVersion: PropTypes.func.isRequired, }).isRequired, allowOtherServers: PropTypes.bool, + componentId: PropTypes.string.isRequired, config: PropTypes.object, currentVersion: PropTypes.string, hasConfigAndLicense: PropTypes.bool.isRequired, latestVersion: PropTypes.string, license: PropTypes.object, minVersion: PropTypes.string, - navigator: PropTypes.object, + navigator: PropTypes.object.isRequired, // TODO remove me serverUrl: PropTypes.string.isRequired, theme: PropTypes.object, }; @@ -93,10 +96,11 @@ export default class SelectServer extends PureComponent { } this.certificateListener = DeviceEventEmitter.addListener('RNFetchBlobCertificate', this.selectCertificate); - this.props.navigator.setOnNavigatorEvent(this.handleNavigatorEvent); telemetry.end(['start:select_server_screen']); telemetry.save(); + + this.navigationEventListener = Navigation.events().bindComponent(this); } componentWillUpdate(nextProps, nextState) { @@ -117,10 +121,19 @@ export default class SelectServer extends PureComponent { } componentWillUnmount() { - this.certificateListener.remove(); if (Platform.OS === 'android') { Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard); } + + this.certificateListener.remove(); + + this.navigationEventListener.remove(); + } + + componentDidDisappear() { + this.setState({ + connected: false, + }); } blur = () => { @@ -145,18 +158,28 @@ export default class SelectServer extends PureComponent { }; goToNextScreen = (screen, title) => { - const {navigator, theme} = this.props; - navigator.push({ - screen, - title, - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarHidden: LocalConfig.AutoSelectServerUrl, - disabledBackGesture: LocalConfig.AutoSelectServerUrl, - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, + const {componentId, theme} = this.props; + + Navigation.push(componentId, { + component: { + name: screen, + options: { + popGesture: !LocalConfig.AutoSelectServerUrl, + topBar: { + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + title: { + color: theme.sidebarHeaderTextColor, + text: title, + }, + visible: !LocalConfig.AutoSelectServerUrl, + }, + }, }, }); }; @@ -244,16 +267,6 @@ export default class SelectServer extends PureComponent { } }; - handleNavigatorEvent = (event) => { - switch (event.id) { - case 'didDisappear': - this.setState({ - connected: false, - }); - break; - } - }; - handleShowClientUpgrade = (upgradeType) => { const {formatMessage} = this.context.intl; const {theme} = this.props; @@ -287,28 +300,13 @@ export default class SelectServer extends PureComponent { }; loginWithCertificate = async () => { - const {navigator} = this.props; - tracker.initialLoad = Date.now(); await this.props.actions.login('credential', 'password'); await this.props.actions.handleSuccessfulLogin(); this.scheduleSessionExpiredNotification(); - navigator.resetTo({ - screen: 'Channel', - title: '', - animated: false, - backButtonTitle: '', - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - }, - }); + this.props.actions.resetToChannel(); }; pingServer = (url, retryWithHttp = true) => { From cbe0c719ac82705aade331aa713899734ef9b7a2 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Wed, 12 Jun 2019 12:26:03 -0700 Subject: [PATCH 04/19] [MM-15874] Add native code to support RNN v2 on Android (#2855) * Remove fix for MM-9233 * MM-15774 Add native code to support RNN v2 on iOS * Android changes for RNN v2 upgrade The activity visibility handling of NotificationsLifecycleFacade is no longer needed in RNN v2. I've moved the lifecycle callbacks we use for handling managed config into ManagedActivityLifecycleCallbacks.java and registered them in MainApplication's onCreate. Also, I've moved and updated the loading and getting of the managed config into MainApplication * Update moduleNames and modulePaths * Use TAG in restrictionsReceiver * Set launch screen onCreate * Comment out registerActivityLifecycleCallbacks for now * Remove setSoftInputMode call as it's handled in the manifest * Remove clearHostOnActivityDestroy as that fix is no longer needed * Rename to canLaunchEntry * Remove replacement of super.onBackPressed() * Remove react-navigation from packager files --- Makefile | 12 +- android/app/build.gradle | 1 + .../com/mattermost/rnbeta/MainActivity.java | 11 +- .../mattermost/rnbeta/MainApplication.java | 118 +++++--- .../ManagedActivityLifecycleCallbacks.java | 141 ++++++++++ .../rnbeta/MattermostManagedModule.java | 2 +- .../rnbeta/NotificationsLifecycleFacade.java | 251 ------------------ android/build.gradle | 6 + android/settings.gradle | 2 +- app/app.js | 4 +- app/mattermost.js | 6 +- fastlane/Fastfile | 4 +- package.json | 3 +- packager/README.md | 1 + packager/moduleNames.js | 34 ++- packager/modulePaths.js | 32 ++- 16 files changed, 294 insertions(+), 334 deletions(-) create mode 100644 android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java delete mode 100644 android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java diff --git a/Makefile b/Makefile index db3d6cf52..186cca33d 100644 --- a/Makefile +++ b/Makefile @@ -160,19 +160,11 @@ run-android: | check-device-android pre-run prepare-android-build ## Runs the ap @if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \ echo Starting React Native packager server; \ npm start & echo Running Android app in development; \ - if [ ! -z ${VARIANT} ]; then \ - react-native run-android --no-packager --variant=${VARIANT}; \ - else \ - react-native run-android --no-packager; \ - fi; \ + npm run android; \ wait; \ else \ echo Running Android app in development; \ - if [ ! -z ${VARIANT} ]; then \ - react-native run-android --no-packager --variant=${VARIANT}; \ - else \ - react-native run-android --no-packager; \ - fi; \ + npm run android; \ fi build: | stop pre-build check-style i18n-extract-ci ## Builds the app for Android & iOS diff --git a/android/app/build.gradle b/android/app/build.gradle index 148f8a12a..2a7cd6794 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -122,6 +122,7 @@ android { applicationId "com.mattermost.rnbeta" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion + missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57_5" versionCode 196 versionName "1.20.0" multiDexEnabled = true diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java index b190dad0c..023da9c70 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java @@ -2,12 +2,14 @@ package com.mattermost.rnbeta; import android.os.Bundle; import android.support.annotation.Nullable; -import com.reactnativenavigation.controllers.SplashActivity; -public class MainActivity extends SplashActivity { +import com.reactnativenavigation.NavigationActivity; + +public class MainActivity extends NavigationActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.launch_screen); /** * Reference: https://stackoverflow.com/questions/7944338/resume-last-activity-when-launcher-icon-is-clicked @@ -23,9 +25,4 @@ public class MainActivity extends SplashActivity { return; } } - - @Override - public int getSplashLayout() { - return R.layout.launch_screen; - } } diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java index 0fabde91c..8521c9efb 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java @@ -5,12 +5,26 @@ import com.mattermost.share.RealPathUtil; import android.app.Activity; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.content.Context; +import android.content.RestrictionsManager; import android.os.Bundle; +import android.util.Log; + import java.io.File; import java.util.Arrays; import java.util.List; +import com.facebook.soloader.SoLoader; +import com.facebook.react.ReactPackage; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactMarker; +import com.facebook.react.bridge.ReactMarkerConstants; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.modules.core.DeviceEventManagerModule; + import com.reactnativedocumentpicker.ReactNativeDocumentPicker; import com.oblador.keychain.KeychainPackage; import com.reactlibrary.RNReactNativeDocViewerPackage; @@ -26,10 +40,6 @@ import com.reactnativecommunity.asyncstorage.AsyncStoragePackage; import com.reactnativecommunity.netinfo.NetInfoPackage; import com.reactnativecommunity.webview.RNCWebViewPackage; import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; - -import com.facebook.react.ReactPackage; -import com.facebook.soloader.SoLoader; - import com.imagepicker.ImagePickerPackage; import com.gnet.bottomsheet.RNBottomSheetPackage; import com.learnium.RNDeviceInfo.RNDeviceInfo; @@ -37,6 +47,8 @@ import com.psykar.cookiemanager.CookieManagerPackage; import com.oblador.vectoricons.VectorIconsPackage; import com.BV.LinearGradient.LinearGradientPackage; import com.reactnativenavigation.NavigationApplication; +import com.reactnativenavigation.react.NavigationReactNativeHost; +import com.reactnativenavigation.react.ReactGateway; import com.wix.reactnativenotifications.RNNotificationsPackage; import com.wix.reactnativenotifications.core.notification.INotificationsApplication; import com.wix.reactnativenotifications.core.notification.IPushNotification; @@ -46,19 +58,9 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.AppLifecycleFacade; import com.wix.reactnativenotifications.core.JsIOHelper; -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactMarker; -import com.facebook.react.bridge.ReactMarkerConstants; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.modules.core.DeviceEventManagerModule; -import android.support.annotation.Nullable; - -import android.util.Log; - public class MainApplication extends NavigationApplication implements INotificationsApplication, INotificationsDrawerApplication { - public NotificationsLifecycleFacade notificationsLifecycleFacade; + public static MainApplication instance; + public Boolean sharedExtensionIsOpened = false; public Boolean replyFromPushNotification = false; @@ -70,6 +72,19 @@ public class MainApplication extends NavigationApplication implements INotificat public long PROCESS_PACKAGES_START; public long PROCESS_PACKAGES_END; + private Bundle mManagedConfig = null; + + @Override + protected ReactGateway createReactGateway() { + ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) { + @Override + protected String getJSMainModuleName() { + return "index"; + } + }; + return new ReactGateway(this, isDebug(), host); + } + @Override public boolean isDebug() { return BuildConfig.DEBUG; @@ -109,45 +124,33 @@ public class MainApplication extends NavigationApplication implements INotificat ); } - @Override - public String getJSMainModuleName() { - return "index"; - } - @Override public void onCreate() { super.onCreate(); instance = this; + // TODO: Right now I get a black screen when resuming the app from the + // background if these callbacks are registered. I'm commenting this out + // to move forward and will come back to this at a later date. + //registerActivityLifecycleCallbacks(new ManagedActivityLifecycleCallbacks()); + // Delete any previous temp files created by the app File tempFolder = new File(getApplicationContext().getCacheDir(), "mmShare"); RealPathUtil.deleteTempFiles(tempFolder); Log.i("ReactNative", "Cleaning temp cache " + tempFolder.getAbsolutePath()); - // Create an object of the custom facade impl - notificationsLifecycleFacade = NotificationsLifecycleFacade.getInstance(); - // Attach it to react-native-navigation - setActivityCallbacks(notificationsLifecycleFacade); - SoLoader.init(this, /* native exopackage */ false); // Uncomment to listen to react markers for build that has telemetry enabled // addReactMarkerListener(); } - @Override - public boolean clearHostOnActivityDestroy(Activity activity) { - // This solves the issue where the splash screen does not go away - // after the app is killed by the OS cause of memory or a long time in the background - return false; - } - @Override public IPushNotification getPushNotification(Context context, Bundle bundle, AppLifecycleFacade defaultFacade, AppLaunchHelper defaultAppLaunchHelper) { return new CustomPushNotification( context, bundle, - notificationsLifecycleFacade, // Instead of defaultFacade!!! + defaultFacade, defaultAppLaunchHelper, new JsIOHelper() ); @@ -158,6 +161,51 @@ public class MainApplication extends NavigationApplication implements INotificat return new CustomPushNotificationDrawer(context, defaultAppLaunchHelper); } + public ReactContext getRunningReactContext() { + final ReactGateway reactGateway = getReactGateway(); + + if (reactGateway == null) { + return null; + } + + return reactGateway + .getReactNativeHost() + .getReactInstanceManager() + .getCurrentReactContext(); + } + + public synchronized Bundle loadManagedConfig(Context ctx) { + if (ctx != null) { + RestrictionsManager myRestrictionsMgr = + (RestrictionsManager) ctx.getSystemService(Context.RESTRICTIONS_SERVICE); + + mManagedConfig = myRestrictionsMgr.getApplicationRestrictions(); + myRestrictionsMgr = null; + + if (mManagedConfig!= null && mManagedConfig.size() > 0) { + return mManagedConfig; + } + + return null; + } + + return null; + } + + public synchronized Bundle getManagedConfig() { + if (mManagedConfig!= null && mManagedConfig.size() > 0) { + return mManagedConfig; + } + + ReactContext ctx = getRunningReactContext(); + + if (ctx != null) { + return loadManagedConfig(ctx); + } + + return null; + } + private void addReactMarkerListener() { ReactMarker.addListener(new ReactMarker.MarkerListener() { @Override @@ -171,7 +219,7 @@ public class MainApplication extends NavigationApplication implements INotificat PROCESS_PACKAGES_END = System.currentTimeMillis(); } else if (name.toString() == ReactMarkerConstants.CONTENT_APPEARED.toString()) { CONTENT_APPEARED = System.currentTimeMillis(); - ReactContext ctx = getReactGateway().getReactContext(); + ReactContext ctx = getRunningReactContext(); if (ctx != null) { WritableMap map = Arguments.createMap(); diff --git a/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java b/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java new file mode 100644 index 000000000..a3fdfe59f --- /dev/null +++ b/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java @@ -0,0 +1,141 @@ +package com.mattermost.rnbeta; + +import android.os.Bundle; +import android.app.Activity; +import android.app.Application.ActivityLifecycleCallbacks; +import android.content.Context; +import android.content.RestrictionsManager; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.util.ArraySet; +import android.util.Log; + +import java.util.Set; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.core.DeviceEventManagerModule; + +public class ManagedActivityLifecycleCallbacks implements ActivityLifecycleCallbacks { + private static final String TAG = ManagedActivityLifecycleCallbacks.class.getSimpleName(); + + private final IntentFilter restrictionsFilter = + new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + + private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context ctx, Intent intent) { + if (ctx != null) { + Bundle managedConfig = MainApplication.instance.loadManagedConfig(ctx); + + // Check current configuration settings, change your app's UI and + // functionality as necessary. + Log.i(TAG, "Managed Configuration Changed"); + sendConfigChanged(managedConfig); + } + } + }; + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + MattermostManagedModule managedModule = MattermostManagedModule.getInstance(); + if (managedModule != null && managedModule.isBlurAppScreenEnabled() && activity != null) { + activity.getWindow().setFlags(LayoutParams.FLAG_SECURE, + LayoutParams.FLAG_SECURE); + } + + Bundle managedConfig = MainApplication.instance.getManagedConfig(); + if (managedConfig != null && activity != null) { + activity.registerReceiver(restrictionsReceiver, restrictionsFilter); + } + } + + @Override + public void onActivityResumed(Activity activity) { + ReactContext ctx = MainApplication.instance.getRunningReactContext(); + Bundle managedConfig = MainApplication.instance.getManagedConfig(); + + if (ctx != null) { + Bundle newConfig = MainApplication.instance.loadManagedConfig(ctx); + if (!equalBundles(newConfig, managedConfig)) { + Log.i(TAG, "onResumed Managed Configuration Changed"); + sendConfigChanged(newConfig); + } + } + } + + @Override + public void onActivityStopped(Activity activity) { + Bundle managedConfig = MainApplication.instance.getManagedConfig(); + + if (managedConfig != null) { + try { + activity.unregisterReceiver(restrictionsReceiver); + } catch (IllegalArgumentException e) { + // Just ignore this cause the receiver wasn't registered for this activity + } + } + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + } + + private void sendConfigChanged(Bundle config) { + Object result = Arguments.fromBundle(config); + ReactContext ctx = MainApplication.instance.getRunningReactContext(); + + if (ctx != null) { + ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("managedConfigDidChange", result); + } + } + + private boolean equalBundles(Bundle one, Bundle two) { + if (one == null || two == null) + return false; + + if(one.size() != two.size()) + return false; + + Set setOne = new ArraySet(); + setOne.addAll(one.keySet()); + setOne.addAll(two.keySet()); + Object valueOne; + Object valueTwo; + + for(String key : setOne) { + if (!one.containsKey(key) || !two.containsKey(key)) + return false; + + valueOne = one.get(key); + valueTwo = two.get(key); + if(valueOne instanceof Bundle && valueTwo instanceof Bundle && + !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) { + return false; + } + else if(valueOne == null) { + if(valueTwo != null) + return false; + } + else if(!valueOne.equals(valueTwo)) + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java b/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java index affdb1fc5..1e73f5e9c 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java @@ -52,7 +52,7 @@ public class MattermostManagedModule extends ReactContextBaseJavaModule { @ReactMethod public void getConfig(final Promise promise) { try { - Bundle config = NotificationsLifecycleFacade.getInstance().getManagedConfig(); + Bundle config = MainApplication.instance.getManagedConfig(); if (config != null) { Object result = Arguments.fromBundle(config); diff --git a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java b/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java deleted file mode 100644 index f6177a2d9..000000000 --- a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.mattermost.rnbeta; - -import android.app.Activity; -import android.content.pm.ActivityInfo; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.RestrictionsManager; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.util.Log; -import android.util.ArraySet; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; -import android.content.res.Configuration; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.modules.core.DeviceEventManagerModule; - -import com.reactnativenavigation.NavigationApplication; -import com.reactnativenavigation.controllers.ActivityCallbacks; -import com.reactnativenavigation.react.ReactGateway; -import com.wix.reactnativenotifications.core.AppLifecycleFacade; - -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - - -public class NotificationsLifecycleFacade extends ActivityCallbacks implements AppLifecycleFacade { - private static final String TAG = NotificationsLifecycleFacade.class.getSimpleName(); - private static NotificationsLifecycleFacade instance; - - private Bundle managedConfig = null; - private Activity mVisibleActivity; - private Set mListeners = new CopyOnWriteArraySet<>(); - - private final IntentFilter restrictionsFilter = - new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); - - private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { - if (context != null) { - // Get the current configuration bundle - RestrictionsManager myRestrictionsMgr = - (RestrictionsManager) context - .getSystemService(Context.RESTRICTIONS_SERVICE); - managedConfig = myRestrictionsMgr.getApplicationRestrictions(); - - // Check current configuration settings, change your app's UI and - // functionality as necessary. - Log.i("ReactNative", "Managed Configuration Changed"); - sendConfigChanged(managedConfig); - } - } - }; - - public static NotificationsLifecycleFacade getInstance() { - if (instance == null) { - instance = new NotificationsLifecycleFacade(); - } - - return instance; - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - MattermostManagedModule managedModule = MattermostManagedModule.getInstance(); - if (managedModule != null && managedModule.isBlurAppScreenEnabled() && activity != null) { - activity.getWindow().setFlags(LayoutParams.FLAG_SECURE, - LayoutParams.FLAG_SECURE); - } - if (managedConfig != null && managedConfig.size() > 0 && activity != null) { - activity.registerReceiver(restrictionsReceiver, restrictionsFilter); - } - - if (activity != null) { - activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - } - } - - @Override - public void onActivityResumed(Activity activity) { - switchToVisible(activity); - - ReactContext ctx = getRunningReactContext(); - if (managedConfig != null && managedConfig.size() > 0 && ctx != null) { - - RestrictionsManager myRestrictionsMgr = - (RestrictionsManager) ctx - .getSystemService(Context.RESTRICTIONS_SERVICE); - - Bundle newConfig = myRestrictionsMgr.getApplicationRestrictions(); - if (!equalBundles(newConfig ,managedConfig)) { - Log.i("ReactNative", "onResumed Managed Configuration Changed"); - managedConfig = newConfig; - sendConfigChanged(managedConfig); - } - } - } - - @Override - public void onActivityPaused(Activity activity) { - switchToInvisible(activity); - } - - @Override - public void onActivityStopped(Activity activity) { - switchToInvisible(activity); - if (managedConfig != null && managedConfig.size() > 0) { - try { - activity.unregisterReceiver(restrictionsReceiver); - } catch (IllegalArgumentException e) { - // Just ignore this cause the receiver wasn't registered for this activity - } - } - } - - @Override - public void onActivityDestroyed(Activity activity) { - switchToInvisible(activity); - } - - @Override - public boolean isReactInitialized() { - return NavigationApplication.instance.isReactContextInitialized(); - } - - @Override - public ReactContext getRunningReactContext() { - final ReactGateway reactGateway = NavigationApplication.instance.getReactGateway(); - if (reactGateway == null || !reactGateway.isInitialized()) { - return null; - } - - return reactGateway.getReactContext(); - } - - @Override - public boolean isAppVisible() { - return mVisibleActivity != null; - } - - @Override - public synchronized void addVisibilityListener(AppVisibilityListener listener) { - mListeners.add(listener); - } - - @Override - public synchronized void removeVisibilityListener(AppVisibilityListener listener) { - mListeners.remove(listener); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - if (mVisibleActivity != null) { - Intent intent = new Intent("onConfigurationChanged"); - intent.putExtra("newConfig", newConfig); - mVisibleActivity.sendBroadcast(intent); - } - } - - private synchronized void switchToVisible(Activity activity) { - if (mVisibleActivity == null) { - mVisibleActivity = activity; - Log.v(TAG, "Activity is now visible ("+activity+")"); - for (AppVisibilityListener listener : mListeners) { - listener.onAppVisible(); - } - } - } - - private synchronized void switchToInvisible(Activity activity) { - if (mVisibleActivity == activity) { - mVisibleActivity = null; - Log.v(TAG, "Activity is now NOT visible ("+activity+")"); - for (AppVisibilityListener listener : mListeners) { - listener.onAppNotVisible(); - } - } - } - - public synchronized void LoadManagedConfig(ReactContext ctx) { - if (ctx != null) { - RestrictionsManager myRestrictionsMgr = - (RestrictionsManager) ctx - .getSystemService(Context.RESTRICTIONS_SERVICE); - - managedConfig = myRestrictionsMgr.getApplicationRestrictions(); - myRestrictionsMgr = null; - } - } - - public synchronized Bundle getManagedConfig() { - if (managedConfig!= null && managedConfig.size() > 0) { - return managedConfig; - } - - ReactContext ctx = getRunningReactContext(); - - if (ctx != null) { - LoadManagedConfig(ctx); - return managedConfig; - } - - return null; - } - - public void sendConfigChanged(Bundle config) { - Object result = Arguments.fromBundle(config); - ReactContext ctx = getRunningReactContext(); - if (ctx != null) { - ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class). - emit("managedConfigDidChange", result); - } - } - - private boolean equalBundles(Bundle one, Bundle two) { - if (one == null || two == null) - return false; - - if(one.size() != two.size()) - return false; - - Set setOne = new ArraySet(); - setOne.addAll(one.keySet()); - setOne.addAll(two.keySet()); - Object valueOne; - Object valueTwo; - - for(String key : setOne) { - if (!one.containsKey(key) || !two.containsKey(key)) - return false; - - valueOne = one.get(key); - valueTwo = two.get(key); - if(valueOne instanceof Bundle && valueTwo instanceof Bundle && - !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) { - return false; - } - else if(valueOne == null) { - if(valueTwo != null) - return false; - } - else if(!valueOne.equals(valueTwo)) - return false; - } - - return true; - } -} diff --git a/android/build.gradle b/android/build.gradle index 3c826c44c..c4991bc58 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -11,6 +11,8 @@ buildscript { repositories { jcenter() google() + mavenLocal() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:3.3.1' @@ -37,6 +39,7 @@ subprojects { allprojects { repositories { google() + mavenCentral() mavenLocal() jcenter() maven { @@ -47,5 +50,8 @@ allprojects { // Local Maven repo containing AARs with JSC library built for Android url "$rootDir/../node_modules/jsc-android/dist" } + maven { + url "https://jitpack.io" + } } } diff --git a/android/settings.gradle b/android/settings.gradle index 185f7b892..a07602e25 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -22,7 +22,7 @@ project(':jail-monkey').projectDir = new File(rootProject.projectDir, '../node_m include ':react-native-local-auth' project(':react-native-local-auth').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-local-auth/android') include ':react-native-navigation' -project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/') +project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/') include ':react-native-image-picker' project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android') include ':react-native-bottom-sheet' diff --git a/app/app.js b/app/app.js index 36baf2a1a..ff4769114 100644 --- a/app/app.js +++ b/app/app.js @@ -35,7 +35,7 @@ export default class App { this.startAppFromPushNotification = false; this.isNotificationsConfigured = false; this.allowOtherServers = true; - this.appStarted = false; + this.canLaunchEntry = true; this.emmEnabled = false; this.performingEMMAuthentication = false; this.translations = null; @@ -276,7 +276,7 @@ export default class App { }; startApp = () => { - if (this.appStarted || this.waitForRehydration) { + if (this.waitForRehydration) { return; } diff --git a/app/mattermost.js b/app/mattermost.js index a92049115..4446484bf 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -481,15 +481,15 @@ const fromPushNotification = Platform.OS === 'android' && Initialization.replyFr if (startedSharedExtension || fromPushNotification) { // Hold on launching Entry screen - app.setAppStarted(true); + app.canLaunchEntry(false); // Listen for when the user opens the app new NativeEventsReceiver().appLaunched(() => { - app.setAppStarted(false); + app.canLaunchEntry(true); launchEntry(); }); } -if (!app.appStarted) { +if (app.canLaunchEntry) { launchEntry(); } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7f8cbd8c3..b6d910302 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -183,7 +183,7 @@ lane :build do |options| build end -desc 'Buid the app for Android and iOS unsigned' +desc 'Build the app for Android and iOS unsigned' lane :unsigned do configure @@ -507,7 +507,7 @@ platform :android do config_mode = ENV['BUILD_FOR_RELEASE'] == 'true' ? 'Release' : 'Debug' gradle( - task: 'assemble', + task: 'app:assemble', build_type: config_mode, project_dir: 'android/' ) diff --git a/package.json b/package.json index 6f4becfd7..665301b3d 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,8 @@ "test:watch": "jest --watch", "test:coverage": "jest --coverage", "updatesnapshot": "jest --updateSnapshot", - "mmjstool": "mmjstool" + "mmjstool": "mmjstool", + "android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug" }, "rnpm": { "assets": [ diff --git a/packager/README.md b/packager/README.md index 3df9d58f1..dd03ad15c 100644 --- a/packager/README.md +++ b/packager/README.md @@ -27,6 +27,7 @@ - remotedev-utils - socketcluster-client - stacktrace-parser + - react-navigation 8. Change development versions of certain files to production ones: - configureStore.dev.js -> configureStore.prod.js - react/cjs/react.development.js -> react/cjs/react.production.min.js diff --git a/packager/moduleNames.js b/packager/moduleNames.js index 2b780a989..a19dc859d 100644 --- a/packager/moduleNames.js +++ b/packager/moduleNames.js @@ -524,16 +524,28 @@ module.exports = [ 'node_modules/react-native-linear-gradient/index.android.js', 'node_modules/react-native-local-auth/LocalAuth.android.js', 'node_modules/react-native-local-auth/index.js', - 'node_modules/react-native-navigation/src/NativeEventsReceiver.js', - 'node_modules/react-native-navigation/src/Navigation.js', - 'node_modules/react-native-navigation/src/PropRegistry.js', - 'node_modules/react-native-navigation/src/Screen.js', - 'node_modules/react-native-navigation/src/ScreenVisibilityListener.js', - 'node_modules/react-native-navigation/src/deprecated/indexDeprecated.android.js', - 'node_modules/react-native-navigation/src/deprecated/platformSpecificDeprecated.android.js', - 'node_modules/react-native-navigation/src/index.js', - 'node_modules/react-native-navigation/src/platformSpecific.android.js', - 'node_modules/react-native-navigation/src/views/sharedElementTransition.android.js', + 'node_modules/react-native-navigation/lib/dist/Navigation.js', + 'node_modules/react-native-navigation/lib/dist/adapters/AppRegistryService.js', + 'node_modules/react-native-navigation/lib/dist/adapters/AssetResolver.js', + 'node_modules/react-native-navigation/lib/dist/adapters/ColorService.js', + 'node_modules/react-native-navigation/lib/dist/adapters/Constants.js', + 'node_modules/react-native-navigation/lib/dist/adapters/NativeCommandsSender.js', + 'node_modules/react-native-navigation/lib/dist/adapters/NativeEventsReceiver.js', + 'node_modules/react-native-navigation/lib/dist/adapters/SharedElement.js', + 'node_modules/react-native-navigation/lib/dist/adapters/TouchablePreview.js', + 'node_modules/react-native-navigation/lib/dist/adapters/UniqueIdProvider.js', + 'node_modules/react-native-navigation/lib/dist/commands/Commands.js', + 'node_modules/react-native-navigation/lib/dist/commands/LayoutTreeCrawler.js', + 'node_modules/react-native-navigation/lib/dist/commands/LayoutTreeParser.js', + 'node_modules/react-native-navigation/lib/dist/commands/OptionsProcessor.js', + 'node_modules/react-native-navigation/lib/dist/components/ComponentRegistry.js', + 'node_modules/react-native-navigation/lib/dist/components/ComponentWrapper.js', + 'node_modules/react-native-navigation/lib/dist/components/Store.js', + 'node_modules/react-native-navigation/lib/dist/events/CommandsObserver.js', + 'node_modules/react-native-navigation/lib/dist/events/ComponentEventsObserver.js', + 'node_modules/react-native-navigation/lib/dist/events/EventsRegistry.js', + 'node_modules/react-native-navigation/lib/dist/index.js', + 'node_modules/react-native-navigation/lib/dist/interfaces/Options.js', 'node_modules/react-native-notifications/index.android.js', 'node_modules/react-native-notifications/notification.android.js', 'node_modules/react-native/Libraries/Animated/src/Animated.js', @@ -742,4 +754,4 @@ module.exports = [ 'node_modules/symbol-observable/lib/ponyfill.js', 'node_modules/tinycolor2/tinycolor.js', 'node_modules/url-parse/index.js', -]; +]; \ No newline at end of file diff --git a/packager/modulePaths.js b/packager/modulePaths.js index 0f657849a..6c18ad900 100644 --- a/packager/modulePaths.js +++ b/packager/modulePaths.js @@ -500,16 +500,28 @@ module.exports = ['./node_modules/app/app.js', './node_modules/node_modules/react-native-linear-gradient/index.android.js', './node_modules/node_modules/react-native-local-auth/LocalAuth.android.js', './node_modules/node_modules/react-native-local-auth/index.js', - './node_modules/node_modules/react-native-navigation/src/NativeEventsReceiver.js', - './node_modules/node_modules/react-native-navigation/src/Navigation.js', - './node_modules/node_modules/react-native-navigation/src/PropRegistry.js', - './node_modules/node_modules/react-native-navigation/src/Screen.js', - './node_modules/node_modules/react-native-navigation/src/ScreenVisibilityListener.js', - './node_modules/node_modules/react-native-navigation/src/deprecated/indexDeprecated.android.js', - './node_modules/node_modules/react-native-navigation/src/deprecated/platformSpecificDeprecated.android.js', - './node_modules/node_modules/react-native-navigation/src/index.js', - './node_modules/node_modules/react-native-navigation/src/platformSpecific.android.js', - './node_modules/node_modules/react-native-navigation/src/views/sharedElementTransition.android.js', + './node_modules/node_modules/react-native-navigation/lib/dist/Navigation.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/AppRegistryService.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/AssetResolver.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/ColorService.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/Constants.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/NativeCommandsSender.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/NativeEventsReceiver.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/SharedElement.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/TouchablePreview.js', + './node_modules/node_modules/react-native-navigation/lib/dist/adapters/UniqueIdProvider.js', + './node_modules/node_modules/react-native-navigation/lib/dist/commands/Commands.js', + './node_modules/node_modules/react-native-navigation/lib/dist/commands/LayoutTreeCrawler.js', + './node_modules/node_modules/react-native-navigation/lib/dist/commands/LayoutTreeParser.js', + './node_modules/node_modules/react-native-navigation/lib/dist/commands/OptionsProcessor.js', + './node_modules/node_modules/react-native-navigation/lib/dist/components/ComponentRegistry.js', + './node_modules/node_modules/react-native-navigation/lib/dist/components/ComponentWrapper.js', + './node_modules/node_modules/react-native-navigation/lib/dist/components/Store.js', + './node_modules/node_modules/react-native-navigation/lib/dist/events/CommandsObserver.js', + './node_modules/node_modules/react-native-navigation/lib/dist/events/ComponentEventsObserver.js', + './node_modules/node_modules/react-native-navigation/lib/dist/events/EventsRegistry.js', + './node_modules/node_modules/react-native-navigation/lib/dist/index.js', + './node_modules/node_modules/react-native-navigation/lib/dist/interfaces/Options.js', './node_modules/node_modules/react-native-notifications/index.android.js', './node_modules/node_modules/react-native-notifications/notification.android.js', './node_modules/node_modules/react-native/Libraries/Animated/src/Animated.js', From 2e423a9f640a0a985e358516f16f291dc23e2245 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Fri, 14 Jun 2019 07:06:14 -0700 Subject: [PATCH 05/19] Fix navigation top bar style (#2885) --- app/actions/navigation.js | 14 ++++++++------ app/mattermost.js | 1 + app/screens/select_server/select_server.js | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/actions/navigation.js b/app/actions/navigation.js index b206b1e3b..32702f58b 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -16,6 +16,9 @@ export function resetToChannel() { component: { name: 'Channel', options: { + statusBar: { + visible: true, + }, topBar: { backButton: { color: theme.sidebarHeaderTextColor, @@ -24,13 +27,11 @@ export function resetToChannel() { background: { color: theme.sidebarHeaderBg, }, - statusBar: { - visible: true, - }, title: { color: theme.sidebarHeaderTextColor, }, visible: false, + height: 0, }, }, }, @@ -55,6 +56,9 @@ export function resetToSelectServer(allowOtherServers) { allowOtherServers, }, options: { + statusBar: { + visible: true, + }, topBar: { backButton: { color: theme.sidebarHeaderTextColor, @@ -63,10 +67,8 @@ export function resetToSelectServer(allowOtherServers) { background: { color: theme.sidebarHeaderBg, }, - statusBar: { - visible: true, - }, visible: false, + height: 0, }, }, }, diff --git a/app/mattermost.js b/app/mattermost.js index 4446484bf..3cc7772ba 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -465,6 +465,7 @@ const launchEntry = () => { }, topBar: { visible: false, + height: 0, }, }, }, diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index 7d77448dd..6dc4cfc02 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -178,6 +178,7 @@ export default class SelectServer extends PureComponent { text: title, }, visible: !LocalConfig.AutoSelectServerUrl, + height: LocalConfig.AutoSelectServerUrl ? 0 : null, }, }, }, From 7d67f1214ea43b32cbfc776f936b433e7bd4b6fc Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Fri, 14 Jun 2019 08:14:26 -0700 Subject: [PATCH 06/19] [MM-16008] Update setNavigatorStyles and replace setOnNavigatorEvent (#2877) * Update setNavigatorStyles * Replace navigator.setOnNavigatorEvent and callbacks * Update unit tests * Save user notify props on componentWillUnmount * Add TODOs about removing componentDidAppear/setNavigatorStyles * check-styles fix * Fix eslint errors --- .../profile_picture_button.test.js.snap | 1 - app/components/profile_picture_button.test.js | 1 - .../safe_area_view/safe_area_view.ios.js | 24 ++- .../sidebars/settings/settings_sidebar.js | 3 + app/screens/about/about.js | 3 +- app/screens/add_reaction/add_reaction.js | 25 +-- .../channel_add_members.js | 23 +-- .../channel_add_members.test.js | 4 +- app/screens/channel_info/channel_info.js | 3 +- .../channel_members/channel_members.js | 23 +-- app/screens/channel_peek/channel_peek.js | 22 ++- app/screens/client_upgrade/client_upgrade.js | 25 +-- app/screens/code/code.js | 3 +- app/screens/create_channel/create_channel.js | 30 +-- app/screens/edit_channel/edit_channel.js | 31 ++-- app/screens/edit_post/edit_post.js | 31 ++-- .../__snapshots__/edit_profile.test.js.snap | 13 -- app/screens/edit_profile/edit_profile.js | 30 +-- app/screens/edit_profile/edit_profile.test.js | 3 +- .../error_teams_list/error_teams_list.js | 25 +-- .../error_teams_list/error_teams_list.test.js | 6 +- app/screens/flagged_posts/flagged_posts.js | 24 +-- .../interactive_dialog/interactive_dialog.js | 30 +-- .../interactive_dialog.test.js | 6 +- app/screens/long_post/long_post.js | 21 +-- app/screens/more_channels/more_channels.js | 31 ++-- .../more_channels/more_channels.test.js | 2 +- app/screens/more_dms/more_dms.js | 27 +-- app/screens/permalink/permalink.js | 22 +-- app/screens/permalink/permalink.test.js | 4 +- app/screens/pinned_posts/pinned_posts.js | 28 ++- .../__snapshots__/reaction_list.test.js.snap | 68 ------- app/screens/reaction_list/reaction_list.js | 16 +- .../reaction_list/reaction_list.test.js | 2 +- .../recent_mentions/recent_mentions.js | 20 +- app/screens/search/search.js | 25 +-- app/screens/select_server/select_server.js | 2 + app/screens/select_team/select_team.js | 35 ++-- app/screens/select_team/select_team.test.js | 4 +- .../selector_screen/selector_screen.js | 5 +- .../selector_screen/selector_screen.test.js | 3 - .../display_settings/display_settings.js | 19 +- .../display_settings/display_settings.test.js | 2 +- app/screens/settings/general/settings.js | 39 ++-- .../notification_settings.js | 3 +- .../notification_settings_auto_responder.js | 22 +-- ...otification_settings_email.android.test.js | 11 +- .../notification_settings_email.ios.test.js | 8 +- .../notification_settings_email_base.js | 22 +-- .../notification_settings_mention_base.js | 22 +-- ...notification_settings_mentions_keywords.js | 20 +- .../notification_settings_mobile_base.js | 21 +-- .../terms_of_service.test.js.snap | 39 ---- .../terms_of_service/terms_of_service.js | 33 ++-- .../terms_of_service/terms_of_service.test.js | 2 +- app/screens/text_preview/text_preview.js | 3 +- app/screens/theme/theme.js | 4 +- app/screens/theme/theme.test.js | 3 - app/screens/thread/thread_base.js | 3 +- app/screens/user_profile/user_profile.js | 31 ++-- app/screens/user_profile/user_profile.test.js | 11 +- app/utils/theme.js | 22 ++- package-lock.json | 174 ++++++++++-------- 63 files changed, 562 insertions(+), 656 deletions(-) diff --git a/app/components/__snapshots__/profile_picture_button.test.js.snap b/app/components/__snapshots__/profile_picture_button.test.js.snap index 3cba2c9bd..c93aadecd 100644 --- a/app/components/__snapshots__/profile_picture_button.test.js.snap +++ b/app/components/__snapshots__/profile_picture_button.test.js.snap @@ -21,7 +21,6 @@ exports[`profile_picture_button should match snapshot 1`] = ` "dismissModal": [MockFunction], "push": [MockFunction], "setButtons": [MockFunction], - "setOnNavigatorEvent": [MockFunction], } } theme={ diff --git a/app/components/profile_picture_button.test.js b/app/components/profile_picture_button.test.js index a10138e75..a46a1a09c 100644 --- a/app/components/profile_picture_button.test.js +++ b/app/components/profile_picture_button.test.js @@ -11,7 +11,6 @@ import {Client4} from 'mattermost-redux/client'; describe('profile_picture_button', () => { const navigator = { - setOnNavigatorEvent: jest.fn(), setButtons: jest.fn(), dismissModal: jest.fn(), push: jest.fn(), diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js index 8477a78b7..b69b77ec9 100644 --- a/app/components/safe_area_view/safe_area_view.ios.js +++ b/app/components/safe_area_view/safe_area_view.ios.js @@ -5,6 +5,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Dimensions, Keyboard, NativeModules, View} from 'react-native'; import SafeArea from 'react-native-safe-area'; +import {Navigation} from 'react-native-navigation'; import {DeviceTypes} from 'app/constants'; import mattermostManaged from 'app/mattermost_managed'; @@ -33,10 +34,6 @@ export default class SafeAreaIos extends PureComponent { constructor(props) { super(props); - if (props.navigator) { - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - } - this.state = { keyboard: false, safeAreaInsets: { @@ -56,6 +53,8 @@ export default class SafeAreaIos extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + Dimensions.addEventListener('change', this.getSafeAreaInsets); this.keyboardDidShowListener = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow); this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide); @@ -70,6 +69,14 @@ export default class SafeAreaIos extends PureComponent { this.mounted = false; } + componentDidAppear() { + this.getSafeAreaInsets(); + } + + componentDidDisappear() { + this.getSafeAreaInsets(); + } + getStatusBarHeight = () => { try { StatusBarManager.getHeight( @@ -106,15 +113,6 @@ export default class SafeAreaIos extends PureComponent { this.setState({keyboard: true}); }; - onNavigatorEvent = (event) => { - switch (event.id) { - case 'willAppear': - case 'didDisappear': - this.getSafeAreaInsets(); - break; - } - }; - renderTopBar = () => { const {safeAreaInsets, statusBarHeight} = this.state; const {headerComponent, excludeHeader, forceTop, navBarBackgroundColor, theme} = this.props; diff --git a/app/components/sidebars/settings/settings_sidebar.js b/app/components/sidebars/settings/settings_sidebar.js index 9d552978b..726b9db4f 100644 --- a/app/components/sidebars/settings/settings_sidebar.js +++ b/app/components/sidebars/settings/settings_sidebar.js @@ -194,6 +194,9 @@ export default class SettingsDrawer extends PureComponent { goToSettings = preventDoubleTap(() => { const {intl} = this.context; + // TODO: Ensure all styles used in app/utils/theme's setNavigatorStyles + // are passed to Settings when this showModal call is updated to RNN v2, + // then remove setNavigatorStyles call in app/screens/settings/general/settings.js this.openModal( 'Settings', intl.formatMessage({id: 'mobile.routes.settings', defaultMessage: 'Settings'}), diff --git a/app/screens/about/about.js b/app/screens/about/about.js index 69aa156a4..dc056e9ec 100644 --- a/app/screens/about/about.js +++ b/app/screens/about/about.js @@ -23,6 +23,7 @@ const MATTERMOST_BUNDLE_IDS = ['com.mattermost.rnbeta', 'com.mattermost.rn']; export default class About extends PureComponent { static propTypes = { + componentId: PropTypes.string, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, navigator: PropTypes.object.isRequired, @@ -31,7 +32,7 @@ export default class About extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } diff --git a/app/screens/add_reaction/add_reaction.js b/app/screens/add_reaction/add_reaction.js index ff4183c2f..f24978bf5 100644 --- a/app/screens/add_reaction/add_reaction.js +++ b/app/screens/add_reaction/add_reaction.js @@ -7,6 +7,7 @@ import { StyleSheet, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import EmojiPicker from 'app/components/emoji_picker'; import {emptyFunction} from 'app/utils/general'; @@ -14,6 +15,7 @@ import {setNavigatorStyles} from 'app/utils/theme'; export default class AddReaction extends PureComponent { static propTypes = { + componentId: PropTypes.string, closeButton: PropTypes.object, navigator: PropTypes.object.isRequired, onEmojiPress: PropTypes.func, @@ -31,15 +33,24 @@ export default class AddReaction extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons({ leftButtons: [{...this.leftButton, icon: props.closeButton}], }); } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); + } + } + + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-edit-post') { + this.close(); } } @@ -49,16 +60,6 @@ export default class AddReaction extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-edit-post': - this.close(); - break; - } - } - }; - handleEmojiPress = (emoji) => { this.props.onEmojiPress(emoji); this.close(); diff --git a/app/screens/channel_add_members/channel_add_members.js b/app/screens/channel_add_members/channel_add_members.js index b7b0da221..11e42ea88 100644 --- a/app/screens/channel_add_members/channel_add_members.js +++ b/app/screens/channel_add_members/channel_add_members.js @@ -9,6 +9,7 @@ import { Platform, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {debounce} from 'mattermost-redux/actions/helpers'; import {General} from 'mattermost-redux/constants'; @@ -33,6 +34,7 @@ export default class ChannelAddMembers extends PureComponent { handleAddChannelMembers: PropTypes.func.isRequired, searchProfiles: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, currentChannelId: PropTypes.string.isRequired, currentChannelGroupConstrained: PropTypes.bool, currentTeamId: PropTypes.string.isRequired, @@ -72,13 +74,14 @@ export default class ChannelAddMembers extends PureComponent { showAsAction: 'always', }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons({ rightButtons: [this.addButton], }); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + const {actions, currentTeamId} = this.props; actions.getTeamStats(currentTeamId); @@ -88,14 +91,20 @@ export default class ChannelAddMembers extends PureComponent { } componentDidUpdate(prevProps) { - const {navigator, theme} = this.props; + const {componentId, theme} = this.props; const {adding, selectedIds} = this.state; const enabled = Object.keys(selectedIds).length > 0 && !adding; this.enableAddOption(enabled); if (theme !== prevProps.theme) { - setNavigatorStyles(navigator, theme); + setNavigatorStyles(componentId, theme); + } + } + + navigationButtonPressed({buttonId}) { + if (buttonId === this.addButton.id) { + this.handleAddMembersPress(); } } @@ -188,14 +197,6 @@ export default class ChannelAddMembers extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === this.addButton.id) { - this.handleAddMembersPress(); - } - } - }; - onSearch = (text) => { if (text) { this.setState({term: text}); diff --git a/app/screens/channel_add_members/channel_add_members.test.js b/app/screens/channel_add_members/channel_add_members.test.js index 700dd6483..3d5eaed74 100644 --- a/app/screens/channel_add_members/channel_add_members.test.js +++ b/app/screens/channel_add_members/channel_add_members.test.js @@ -24,9 +24,9 @@ describe('ChannelAddMembers', () => { navigator: { pop: jest.fn(), setButtons: jest.fn(), - setOnNavigatorEvent: jest.fn(), }, theme: Preferences.THEMES.default, + componentId: 'component-id', }; test('should render without error and call functions on mount', () => { @@ -39,8 +39,6 @@ describe('ChannelAddMembers', () => { expect(baseProps.navigator.setButtons).toBeCalledTimes(2); expect(baseProps.navigator.setButtons.mock.calls[0][0]).toEqual({rightButtons: [button]}); expect(baseProps.navigator.setButtons.mock.calls[1][0]).toEqual({rightButtons: [button]}); - - expect(baseProps.navigator.setOnNavigatorEvent).toBeCalledTimes(1); }); test('should match state on clearSearch', () => { diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index d2e892cc8..cb2e3b08e 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -43,6 +43,7 @@ export default class ChannelInfo extends PureComponent { handleSelectChannel: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, }), + componentId: PropTypes.string, viewArchivedChannels: PropTypes.bool.isRequired, canDeleteChannel: PropTypes.bool.isRequired, currentChannel: PropTypes.object.isRequired, @@ -82,7 +83,7 @@ export default class ChannelInfo extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } let isFavorite = this.state.isFavorite; diff --git a/app/screens/channel_members/channel_members.js b/app/screens/channel_members/channel_members.js index 1eb4fc40d..607582710 100644 --- a/app/screens/channel_members/channel_members.js +++ b/app/screens/channel_members/channel_members.js @@ -9,6 +9,7 @@ import { View, } from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {debounce} from 'mattermost-redux/actions/helpers'; import {General} from 'mattermost-redux/constants'; @@ -32,6 +33,7 @@ export default class ChannelMembers extends PureComponent { handleRemoveChannelMembers: PropTypes.func.isRequired, searchProfiles: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, canManageUsers: PropTypes.bool.isRequired, currentChannelId: PropTypes.string.isRequired, currentChannelMembers: PropTypes.array, @@ -67,7 +69,6 @@ export default class ChannelMembers extends PureComponent { title: context.intl.formatMessage({id: 'channel_members_modal.remove', defaultMessage: 'Remove'}), }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); if (props.canManageUsers) { props.navigator.setButtons({ rightButtons: [this.removeButton], @@ -76,18 +77,26 @@ export default class ChannelMembers extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.getProfiles(); } componentDidUpdate(prevProps) { - const {navigator, theme} = this.props; + const {componentId, theme} = this.props; const {removing, selectedIds} = this.state; const enabled = Object.keys(selectedIds).length > 0 && !removing; this.enableRemoveOption(enabled); if (theme !== prevProps.theme) { - setNavigatorStyles(navigator, theme); + setNavigatorStyles(componentId, theme); + } + } + + navigationButtonPressed({buttonId}) { + if (buttonId === this.removeButton.id) { + this.handleRemoveMembersPress(); } } @@ -188,14 +197,6 @@ export default class ChannelMembers extends PureComponent { this.setState({loading: false, profiles: [...profiles, ...data]}); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === this.removeButton.id) { - this.handleRemoveMembersPress(); - } - } - }; - onSearch = (text) => { if (text) { this.setState({term: text}); diff --git a/app/screens/channel_peek/channel_peek.js b/app/screens/channel_peek/channel_peek.js index 3fe98c4be..081952558 100644 --- a/app/screens/channel_peek/channel_peek.js +++ b/app/screens/channel_peek/channel_peek.js @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Platform, View} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {getLastPostIndex} from 'mattermost-redux/utils/post_list'; @@ -32,13 +33,16 @@ export default class ChannelPeek extends PureComponent { constructor(props) { super(props); - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.actions.loadPostsIfNecessaryWithRetry(props.channelId); this.state = { visiblePostIds: this.getVisiblePostIds(props), }; } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + componentWillReceiveProps(nextProps) { const {postIds: nextPostIds} = nextProps; @@ -53,19 +57,17 @@ export default class ChannelPeek extends PureComponent { }); } + navigationButtonPressed({buttonId}) { + if (buttonId === 'action-mark-as-read') { + const {actions, channelId} = this.props; + actions.markChannelViewedAndRead(channelId); + } + } + getVisiblePostIds = (props) => { return props.postIds.slice(0, 15); }; - onNavigatorEvent = (event) => { - if (event.type === 'PreviewActionPress') { - if (event.id === 'action-mark-as-read') { - const {actions, channelId} = this.props; - actions.markChannelViewedAndRead(channelId); - } - } - }; - render() { const { channelId, diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js index ac7d6608b..9ce684d33 100644 --- a/app/screens/client_upgrade/client_upgrade.js +++ b/app/screens/client_upgrade/client_upgrade.js @@ -12,6 +12,7 @@ import { View, } from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; @@ -26,6 +27,7 @@ export default class ClientUpgrade extends PureComponent { logError: PropTypes.func.isRequired, setLastUpgradeCheck: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, currentVersion: PropTypes.string, closeAction: PropTypes.func, userCheckedForUpgrade: PropTypes.bool, @@ -44,13 +46,14 @@ export default class ClientUpgrade extends PureComponent { constructor(props) { super(props); - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); this.state = { upgradeType: UpgradeTypes.NO_UPGRADE, }; } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + if (this.props.userCheckedForUpgrade) { this.checkUpgrade(this.props); } @@ -58,7 +61,15 @@ export default class ClientUpgrade extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); + } + } + + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-upgrade') { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); } } @@ -117,16 +128,6 @@ export default class ClientUpgrade extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-upgrade') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } - } - }; - renderMustUpgrade() { const {theme} = this.props; const styles = getStyleFromTheme(theme); diff --git a/app/screens/code/code.js b/app/screens/code/code.js index 414933cd8..831538c8a 100644 --- a/app/screens/code/code.js +++ b/app/screens/code/code.js @@ -18,6 +18,7 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class Code extends React.PureComponent { static propTypes = { + componentId: PropTypes.string, navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, content: PropTypes.string.isRequired, @@ -29,7 +30,7 @@ export default class Code extends React.PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } diff --git a/app/screens/create_channel/create_channel.js b/app/screens/create_channel/create_channel.js index 14cc18317..18c379e73 100644 --- a/app/screens/create_channel/create_channel.js +++ b/app/screens/create_channel/create_channel.js @@ -8,6 +8,7 @@ import { Keyboard, InteractionManager, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {General, RequestStatus} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -17,6 +18,7 @@ import {setNavigatorStyles} from 'app/utils/theme'; export default class CreateChannel extends PureComponent { static propTypes = { + componentId: PropTypes.string, navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, deviceWidth: PropTypes.number.isRequired, @@ -72,17 +74,17 @@ export default class CreateChannel extends PureComponent { buttons.leftButtons = [this.left]; } - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons(buttons); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); this.emitCanCreateChannel(false); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } const {createChannelRequest} = nextProps; @@ -109,6 +111,17 @@ export default class CreateChannel extends PureComponent { } } + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'close-new-channel': + this.close(!this.props.closeButton); + break; + case 'create-channel': + this.onCreateChannel(); + break; + } + } + close = (goBack = false) => { if (goBack) { this.props.navigator.pop({animated: true}); @@ -149,19 +162,6 @@ export default class CreateChannel extends PureComponent { this.props.actions.handleCreateChannel(displayName, purpose, header, this.props.channelType); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-new-channel': - this.close(!this.props.closeButton); - break; - case 'create-channel': - this.onCreateChannel(); - break; - } - } - }; - onDisplayNameChange = (displayName) => { this.setState({displayName}); }; diff --git a/app/screens/edit_channel/edit_channel.js b/app/screens/edit_channel/edit_channel.js index b992de2dc..137324c5a 100644 --- a/app/screens/edit_channel/edit_channel.js +++ b/app/screens/edit_channel/edit_channel.js @@ -8,6 +8,7 @@ import { Keyboard, InteractionManager, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {General, RequestStatus} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -56,6 +57,7 @@ export default class EditChannel extends PureComponent { getChannel: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, }), + componentId: PropTypes.string, navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, deviceWidth: PropTypes.number.isRequired, @@ -102,17 +104,18 @@ export default class EditChannel extends PureComponent { rightButtons: [this.rightButton], }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons(buttons); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.emitCanUpdateChannel(false); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } const {updateChannelRequest} = nextProps; @@ -139,6 +142,17 @@ export default class EditChannel extends PureComponent { } } + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'close-edit-channel': + this.close(); + break; + case 'edit-channel': + this.onUpdateChannel(); + break; + } + } + close = () => { const {channel: {type}} = this.props; const isDirect = type === General.DM_CHANNEL || type === General.GM_CHANNEL; @@ -238,19 +252,6 @@ export default class EditChannel extends PureComponent { } }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-edit-channel': - this.close(); - break; - case 'edit-channel': - this.onUpdateChannel(); - break; - } - } - }; - onDisplayNameChange = (displayName) => { this.setState({displayName}); }; diff --git a/app/screens/edit_post/edit_post.js b/app/screens/edit_post/edit_post.js index eb053d287..8a7af8838 100644 --- a/app/screens/edit_post/edit_post.js +++ b/app/screens/edit_post/edit_post.js @@ -7,6 +7,7 @@ import { Platform, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import ErrorText from 'app/components/error_text'; import Loading from 'app/components/loading'; @@ -22,6 +23,7 @@ export default class EditPost extends PureComponent { actions: PropTypes.shape({ editPost: PropTypes.func.isRequired, }), + componentId: PropTypes.string, closeButton: PropTypes.object, deviceHeight: PropTypes.number, deviceWidth: PropTypes.number, @@ -50,7 +52,6 @@ export default class EditPost extends PureComponent { this.state = {message: props.post.message}; this.rightButton.title = context.intl.formatMessage({id: 'edit_post.save', defaultMessage: 'Save'}); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons({ leftButtons: [{...this.leftButton, icon: props.closeButton}], rightButtons: [this.rightButton], @@ -58,12 +59,14 @@ export default class EditPost extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.focus(); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } const {editPostRequest} = nextProps; @@ -87,6 +90,17 @@ export default class EditPost extends PureComponent { } } + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'close-edit-post': + this.close(); + break; + case 'edit-post': + this.onEditPost(); + break; + } + } + close = () => { this.props.navigator.dismissModal({ animationType: 'slide-down', @@ -121,19 +135,6 @@ export default class EditPost extends PureComponent { this.props.actions.editPost(post); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-edit-post': - this.close(); - break; - case 'edit-post': - this.onEditPost(); - break; - } - } - }; - onPostChangeText = (message) => { this.setState({message}); if (message) { diff --git a/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap b/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap index d97a63400..9c8aa9c0e 100644 --- a/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap +++ b/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap @@ -52,19 +52,6 @@ exports[`edit_profile should match snapshot 1`] = ` }, ], }, - "setOnNavigatorEvent": [MockFunction] { - "calls": Array [ - Array [ - [Function], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, } } onShowFileSizeWarning={[Function]} diff --git a/app/screens/edit_profile/edit_profile.js b/app/screens/edit_profile/edit_profile.js index 0f6927ef5..bfae5a109 100644 --- a/app/screens/edit_profile/edit_profile.js +++ b/app/screens/edit_profile/edit_profile.js @@ -8,6 +8,7 @@ import {Alert, View} from 'react-native'; import RNFetchBlob from 'rn-fetch-blob'; import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; import {DocumentPickerUtil} from 'react-native-document-picker'; +import {Navigation} from 'react-native-navigation'; import {Client4} from 'mattermost-redux/client'; @@ -114,7 +115,6 @@ export default class EditProfile extends PureComponent { }; this.rightButton.title = context.intl.formatMessage({id: t('mobile.account.settings.save'), defaultMessage: 'Save'}); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons(buttons); this.state = { @@ -127,6 +127,21 @@ export default class EditProfile extends PureComponent { }; } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'update-profile': + this.submitUser(); + break; + case 'close-settings': + this.close(); + break; + } + } + canUpdate = (updatedField) => { const {currentUser} = this.props; const keys = Object.keys(this.state); @@ -276,19 +291,6 @@ export default class EditProfile extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'update-profile': - this.submitUser(); - break; - case 'close-settings': - this.close(); - break; - } - } - }; - onShowFileSizeWarning = (filename) => { const {formatMessage} = this.context.intl; const fileSizeWarning = formatMessage({ diff --git a/app/screens/edit_profile/edit_profile.test.js b/app/screens/edit_profile/edit_profile.test.js index 69682f745..c2b2fe3fc 100644 --- a/app/screens/edit_profile/edit_profile.test.js +++ b/app/screens/edit_profile/edit_profile.test.js @@ -18,7 +18,6 @@ jest.mock('app/utils/theme', () => { describe('edit_profile', () => { const navigator = { - setOnNavigatorEvent: jest.fn(), setButtons: jest.fn(), dismissModal: jest.fn(), push: jest.fn(), @@ -46,6 +45,7 @@ describe('edit_profile', () => { position: 'position', }, commandType: 'ShowModal', + componentId: 'component-id', }; test('should match snapshot', async () => { @@ -59,7 +59,6 @@ describe('edit_profile', () => { test('should match state on handleRemoveProfileImage', () => { const newNavigator = { dismissModal: jest.fn(), - setOnNavigatorEvent: jest.fn(), setButtons: jest.fn(), }; const wrapper = shallow( diff --git a/app/screens/error_teams_list/error_teams_list.js b/app/screens/error_teams_list/error_teams_list.js index 7d303751b..a795920c8 100644 --- a/app/screens/error_teams_list/error_teams_list.js +++ b/app/screens/error_teams_list/error_teams_list.js @@ -7,6 +7,7 @@ import { InteractionManager, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import FailedNetworkAction from 'app/components/failed_network_action'; import Loading from 'app/components/loading'; @@ -32,22 +33,33 @@ export default class ErrorTeamsList extends PureComponent { logout: PropTypes.func.isRequired, selectDefaultTeam: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, navigator: PropTypes.object, theme: PropTypes.object, }; constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); this.state = { loading: false, }; } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); + } + } + + navigationButtonPressed({buttonId}) { + const {logout} = this.props.actions; + if (buttonId === 'logout') { + InteractionManager.runAfterInteractions(logout); } } @@ -83,15 +95,6 @@ export default class ErrorTeamsList extends PureComponent { } } - 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); diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js index dcf9af079..1a8c053dc 100644 --- a/app/screens/error_teams_list/error_teams_list.test.js +++ b/app/screens/error_teams_list/error_teams_list.test.js @@ -10,10 +10,6 @@ import FailedNetworkAction from 'app/components/failed_network_action'; import ErrorTeamsList from './error_teams_list'; describe('ErrorTeamsList', () => { - const navigator = { - setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function - }; - const loadMe = async () => { return { data: {}, @@ -27,8 +23,8 @@ describe('ErrorTeamsList', () => { logout: () => {}, // eslint-disable-line no-empty-function selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function }, + componentId: 'component-id', theme: Preferences.THEMES.default, - navigator, }; test('should match snapshot', () => { diff --git a/app/screens/flagged_posts/flagged_posts.js b/app/screens/flagged_posts/flagged_posts.js index c12964ed8..ca5b27681 100644 --- a/app/screens/flagged_posts/flagged_posts.js +++ b/app/screens/flagged_posts/flagged_posts.js @@ -11,6 +11,7 @@ import { SafeAreaView, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {isDateLine, getDateForDateLine} from 'mattermost-redux/utils/post_list'; @@ -54,11 +55,22 @@ export default class FlaggedPosts extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.actions.clearSearch(); props.actions.getFlaggedPosts(); } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-settings') { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); + } + } + goToThread = (post) => { const {actions, navigator, theme} = this.props; const channelId = post.channel_id; @@ -108,16 +120,6 @@ export default class FlaggedPosts extends PureComponent { keyExtractor = (item) => item; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } - } - }; - previewPost = (post) => { Keyboard.dismiss(); diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js index 8f181a89d..eda0cf7ca 100644 --- a/app/screens/interactive_dialog/interactive_dialog.js +++ b/app/screens/interactive_dialog/interactive_dialog.js @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {ScrollView, View} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {checkDialogElementForError, checkIfErrorsMatchElements} from 'mattermost-redux/utils/integration_utils'; @@ -16,6 +17,7 @@ import DialogElement from './dialog_element.js'; export default class InteractiveDialog extends PureComponent { static propTypes = { + componentId: PropTypes.string, url: PropTypes.string.isRequired, callbackId: PropTypes.string, elements: PropTypes.arrayOf(PropTypes.object).isRequired, @@ -36,8 +38,6 @@ export default class InteractiveDialog extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - const values = {}; props.elements.forEach((e) => { values[e.name] = e.default || null; @@ -50,19 +50,21 @@ export default class InteractiveDialog extends PureComponent { }; } - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'submit-dialog': - this.handleSubmit(); - break; - case 'close-dialog': - this.notifyOnCancelIfNeeded(); - this.handleHide(); - break; - } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'submit-dialog': + this.handleSubmit(); + break; + case 'close-dialog': + this.notifyOnCancelIfNeeded(); + this.handleHide(); + break; } - }; + } handleSubmit = async () => { const {elements} = this.props; diff --git a/app/screens/interactive_dialog/interactive_dialog.test.js b/app/screens/interactive_dialog/interactive_dialog.test.js index 7f36358bb..4219edf33 100644 --- a/app/screens/interactive_dialog/interactive_dialog.test.js +++ b/app/screens/interactive_dialog/interactive_dialog.test.js @@ -21,9 +21,9 @@ describe('InteractiveDialog', () => { submitInteractiveDialog: jest.fn(), }, navigator: { - setOnNavigatorEvent: jest.fn(), dismissModal: jest.fn(), }, + componentId: 'component-id', }; test('should set default values', async () => { @@ -74,7 +74,7 @@ describe('InteractiveDialog', () => { cancelled: true, }; - wrapper.instance().onNavigatorEvent({type: 'NavBarButtonPress', id: 'close-dialog'}); + wrapper.instance().navigationButtonPressed({buttonId: 'close-dialog'}); expect(submitInteractiveDialog).toHaveBeenCalledTimes(1); expect(submitInteractiveDialog).toHaveBeenCalledWith(dialog); }); @@ -89,7 +89,7 @@ describe('InteractiveDialog', () => { />, ); - wrapper.instance().onNavigatorEvent({type: 'NavBarButtonPress', id: 'close-dialog'}); + wrapper.instance().navigationButtonPressed({buttonId: 'close-dialog'}); expect(submitInteractiveDialog).not.toHaveBeenCalled(); }); }); diff --git a/app/screens/long_post/long_post.js b/app/screens/long_post/long_post.js index c28f86fd1..554227e67 100644 --- a/app/screens/long_post/long_post.js +++ b/app/screens/long_post/long_post.js @@ -11,6 +11,7 @@ import { import {intlShape} from 'react-intl'; import * as Animatable from 'react-native-animatable'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; +import {Navigation} from 'react-native-navigation'; import FileAttachmentList from 'app/components/file_attachment_list'; import FormattedText from 'app/components/formatted_text'; @@ -65,10 +66,14 @@ export default class LongPost extends PureComponent { intl: intlShape.isRequired, }; - constructor(props) { - super(props); + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + navigationButtonPressed({buttonId}) { + if (buttonId === 'backPress') { + this.handleClose(); + } } goToThread = preventDoubleTap((post) => { @@ -117,16 +122,6 @@ export default class LongPost extends PureComponent { } }; - onNavigatorEvent = (event) => { - switch (event.id) { - case 'backPress': - this.handleClose(); - break; - default: - break; - } - }; - renderFileAttachments(style) { const { fileIds, diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js index 1d9a79a1f..0330b8c59 100644 --- a/app/screens/more_channels/more_channels.js +++ b/app/screens/more_channels/more_channels.js @@ -5,6 +5,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import {Platform, View} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {debounce} from 'mattermost-redux/actions/helpers'; import {General} from 'mattermost-redux/constants'; @@ -29,6 +30,7 @@ export default class MoreChannels extends PureComponent { searchChannels: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, canCreateChannels: PropTypes.bool.isRequired, channels: PropTypes.array, closeButton: PropTypes.object, @@ -79,11 +81,12 @@ export default class MoreChannels extends PureComponent { buttons.rightButtons = [this.rightButton]; } - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons(buttons); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.doGetChannels(); } @@ -92,7 +95,7 @@ export default class MoreChannels extends PureComponent { let channels; if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } if (nextProps.channels !== this.props.channels) { @@ -107,6 +110,17 @@ export default class MoreChannels extends PureComponent { } } + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'close-more-channels': + this.close(); + break; + case 'create-pub-channel': + this.onCreateChannel(); + break; + } + } + cancelSearch = () => { const {channels} = this.props; @@ -166,19 +180,6 @@ export default class MoreChannels extends PureComponent { this.setState({loading: false}); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-more-channels': - this.close(); - break; - case 'create-pub-channel': - this.onCreateChannel(); - break; - } - } - }; - onSelectChannel = async (id) => { const {intl} = this.context; const {actions, currentTeamId, currentUserId} = this.props; diff --git a/app/screens/more_channels/more_channels.test.js b/app/screens/more_channels/more_channels.test.js index 51787ffcc..32eaee752 100644 --- a/app/screens/more_channels/more_channels.test.js +++ b/app/screens/more_channels/more_channels.test.js @@ -12,7 +12,6 @@ jest.mock('react-intl'); describe('MoreChannels', () => { const navigator = { - setOnNavigatorEvent: jest.fn(), setButtons: jest.fn(), dismissModal: jest.fn(), push: jest.fn(), @@ -35,6 +34,7 @@ describe('MoreChannels', () => { currentTeamId: 'current_team_id', navigator, theme: Preferences.THEMES.default, + componentId: 'component-id', }; test('should match snapshot', () => { diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index f16f5e99a..1af96f318 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -5,6 +5,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import {Platform, View} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {debounce} from 'mattermost-redux/actions/helpers'; import {General} from 'mattermost-redux/constants'; @@ -39,6 +40,7 @@ export default class MoreDirectMessages extends PureComponent { searchProfiles: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, allProfiles: PropTypes.object.isRequired, currentDisplayName: PropTypes.string, currentTeamId: PropTypes.string.isRequired, @@ -70,23 +72,32 @@ export default class MoreDirectMessages extends PureComponent { selectedCount: 0, }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); this.updateNavigationButtons(false, context); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.getProfiles(); } componentDidUpdate(prevProps) { - const {navigator, theme} = this.props; + const {componentId, theme} = this.props; const {selectedCount, startingConversation} = this.state; const canStart = selectedCount > 0 && !startingConversation; this.updateNavigationButtons(canStart); if (theme !== prevProps.theme) { - setNavigatorStyles(navigator, theme); + setNavigatorStyles(componentId, theme); + } + } + + navigationButtonPressed({buttonId}) { + if (buttonId === START_BUTTON) { + this.startConversation(); + } else if (buttonId === CLOSE_BUTTON) { + this.close(); } } @@ -235,16 +246,6 @@ export default class MoreDirectMessages extends PureComponent { return !result.error; }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === START_BUTTON) { - this.startConversation(); - } else if (event.id === CLOSE_BUTTON) { - this.close(); - } - } - }; - onSearch = (text) => { if (text) { this.setState({term: text}); diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js index 81f3f2763..38b688174 100644 --- a/app/screens/permalink/permalink.js +++ b/app/screens/permalink/permalink.js @@ -13,6 +13,7 @@ import {intlShape} from 'react-intl'; import * as Animatable from 'react-native-animatable'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import AwesomeIcon from 'react-native-vector-icons/FontAwesome'; +import {Navigation} from 'react-native-navigation'; import {General} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -57,6 +58,7 @@ export default class Permalink extends PureComponent { setChannelDisplayName: PropTypes.func.isRequired, setChannelLoading: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, channelId: PropTypes.string, channelIsArchived: PropTypes.bool, channelName: PropTypes.string, @@ -131,8 +133,6 @@ export default class Permalink extends PureComponent { loading = false; } - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - this.state = { title: channelName, loading, @@ -146,6 +146,8 @@ export default class Permalink extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.mounted = true; if (this.state.loading) { @@ -163,6 +165,12 @@ export default class Permalink extends PureComponent { this.mounted = false; } + navigationButtonPressed({buttonId}) { + if (buttonId === 'backPress') { + this.handleClose(); + } + } + goToThread = preventDoubleTap((post) => { const {actions, navigator, theme} = this.props; const channelId = post.channel_id; @@ -315,16 +323,6 @@ export default class Permalink extends PureComponent { } }; - onNavigatorEvent = (event) => { - switch (event.id) { - case 'backPress': - this.handleClose(); - break; - default: - break; - } - }; - retry = () => { if (this.mounted) { this.setState({loading: true, error: null, retry: false}); diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js index e639645c6..5cc33b09c 100644 --- a/app/screens/permalink/permalink.test.js +++ b/app/screens/permalink/permalink.test.js @@ -16,7 +16,6 @@ describe('Permalink', () => { dismissModal: jest.fn(), push: jest.fn(), resetTo: jest.fn(), - setOnNavigatorEvent: jest.fn(), }; const actions = { @@ -48,6 +47,7 @@ describe('Permalink', () => { onPress: jest.fn(), postIds: ['post_id_1', 'focused_post_id', 'post_id_3'], theme: Preferences.THEMES.default, + componentId: 'component-id', }; test('should match snapshot', () => { @@ -82,7 +82,7 @@ describe('Permalink', () => { ); wrapper.instance().handleClose = jest.fn(); - wrapper.instance().onNavigatorEvent({id: 'backPress'}); + wrapper.instance().navigationButtonPressed({buttonId: 'backPress'}); expect(wrapper.instance().handleClose).toHaveBeenCalledTimes(1); }); diff --git a/app/screens/pinned_posts/pinned_posts.js b/app/screens/pinned_posts/pinned_posts.js index 121235508..899331ebb 100644 --- a/app/screens/pinned_posts/pinned_posts.js +++ b/app/screens/pinned_posts/pinned_posts.js @@ -11,6 +11,7 @@ import { SafeAreaView, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {isDateLine, getDateForDateLine} from 'mattermost-redux/utils/post_list'; @@ -27,6 +28,7 @@ import noResultsImage from 'assets/images/no_results/pin.png'; export default class PinnedPosts extends PureComponent { static propTypes = { + componentId: PropTypes.string, actions: PropTypes.shape({ clearSearch: PropTypes.func.isRequired, loadChannelsByTeamName: PropTypes.func.isRequired, @@ -52,18 +54,22 @@ export default class PinnedPosts extends PureComponent { intl: intlShape.isRequired, }; - constructor(props) { - super(props); - - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - } - componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + const {actions, currentChannelId} = this.props; actions.clearSearch(); actions.getPinnedPosts(currentChannelId); } + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-settings') { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); + } + } + goToThread = (post) => { const {actions, navigator, theme} = this.props; const channelId = post.channel_id; @@ -113,16 +119,6 @@ export default class PinnedPosts extends PureComponent { keyExtractor = (item) => item; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } - } - }; - previewPost = (post) => { Keyboard.dismiss(); diff --git a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap index 60ce984e2..bb73b1098 100644 --- a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap +++ b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap @@ -24,23 +24,6 @@ exports[`ReactionList should match snapshot 1`] = ` > { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-reaction-list') { - this.close(); - } + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-reaction-list') { + this.close(); } - }; + } close = () => { this.props.navigator.dismissModal({ diff --git a/app/screens/reaction_list/reaction_list.test.js b/app/screens/reaction_list/reaction_list.test.js index 8f1eba9ae..a1fbdb093 100644 --- a/app/screens/reaction_list/reaction_list.test.js +++ b/app/screens/reaction_list/reaction_list.test.js @@ -17,11 +17,11 @@ describe('ReactionList', () => { getMissingProfilesByIds: jest.fn(), }, allUserIds: ['user_id_1', 'user_id_2'], - navigator: {setOnNavigatorEvent: jest.fn()}, reactions: {'user_id_1-smile': {emoji_name: 'smile', user_id: 'user_id_1'}, 'user_id_2-+1': {emoji_name: '+1', user_id: 'user_id_2'}}, theme: Preferences.THEMES.default, teammateNameDisplay: 'username', userProfiles: [{id: 'user_id_1', username: 'username_1'}, {id: 'user_id_2', username: 'username_2'}], + componentId: 'component-id', }; test('should match snapshot', () => { diff --git a/app/screens/recent_mentions/recent_mentions.js b/app/screens/recent_mentions/recent_mentions.js index 2bcef54d1..e836194d9 100644 --- a/app/screens/recent_mentions/recent_mentions.js +++ b/app/screens/recent_mentions/recent_mentions.js @@ -11,6 +11,7 @@ import { StyleSheet, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {isDateLine, getDateForDateLine} from 'mattermost-redux/utils/post_list'; @@ -54,11 +55,14 @@ export default class RecentMentions extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.actions.clearSearch(); props.actions.getRecentMentions(); } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + goToThread = (post) => { const {actions, navigator, theme} = this.props; const channelId = post.channel_id; @@ -108,15 +112,13 @@ export default class RecentMentions extends PureComponent { keyExtractor = (item) => item; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-settings') { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); } - }; + } previewPost = (post) => { Keyboard.dismiss(); diff --git a/app/screens/search/search.js b/app/screens/search/search.js index 9e98ddd73..23ef8a56b 100644 --- a/app/screens/search/search.js +++ b/app/screens/search/search.js @@ -12,6 +12,7 @@ import { View, } from 'react-native'; import AwesomeIcon from 'react-native-vector-icons/FontAwesome'; +import {Navigation} from 'react-native-navigation'; import {debounce} from 'mattermost-redux/actions/helpers'; import {RequestStatus} from 'mattermost-redux/constants'; @@ -85,8 +86,6 @@ export default class Search extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - this.contentOffsetY = 0; this.state = { channelName: '', @@ -97,6 +96,8 @@ export default class Search extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + if (this.props.initialValue) { this.search(this.props.initialValue); } @@ -140,6 +141,16 @@ export default class Search extends PureComponent { } } + navigationButtonPressed({buttonId}) { + if (buttonId === 'backPress') { + if (this.state.preview) { + this.refs.preview.handleClose(); + } else { + this.props.navigator.dismissModal(); + } + } + } + archivedIndicator = (postID, style) => { const channelIsArchived = this.props.archivedPostIds.includes(postID); let archivedIndicator = null; @@ -298,16 +309,6 @@ export default class Search extends PureComponent { } }, 100); - onNavigatorEvent = (event) => { - if (event.id === 'backPress') { - if (this.state.preview) { - this.refs.preview.handleClose(); - } else { - this.props.navigator.dismissModal(); - } - } - }; - previewPost = (post) => { Keyboard.dismiss(); diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index 6dc4cfc02..91e9458e1 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -84,6 +84,8 @@ export default class SelectServer extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + const {allowOtherServers, serverUrl} = this.props; if (!allowOtherServers && serverUrl) { // If the app is managed or AutoSelectServerUrl is true in the Config, the server url is set and the user can't change it diff --git a/app/screens/select_team/select_team.js b/app/screens/select_team/select_team.js index d6d9971b8..54661ee00 100644 --- a/app/screens/select_team/select_team.js +++ b/app/screens/select_team/select_team.js @@ -10,6 +10,7 @@ import { TouchableOpacity, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import {RequestStatus} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -45,6 +46,7 @@ export default class SelectTeam extends PureComponent { joinTeam: PropTypes.func.isRequired, logout: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, currentUrl: PropTypes.string.isRequired, navigator: PropTypes.object, userWithoutTeams: PropTypes.bool, @@ -59,7 +61,6 @@ export default class SelectTeam extends PureComponent { constructor(props) { super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); this.state = { loading: false, @@ -71,12 +72,14 @@ export default class SelectTeam extends PureComponent { } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.getTeams(); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } if (this.props.teams !== nextProps.teams) { @@ -84,6 +87,19 @@ export default class SelectTeam extends PureComponent { } } + navigationButtonPressed({buttonId}) { + const {logout} = this.props.actions; + + switch (buttonId) { + case 'close-teams': + this.close(); + break; + case 'logout': + InteractionManager.runAfterInteractions(logout); + break; + } + } + getTeams = () => { this.setState({loading: true}); this.props.actions.getTeams(this.state.page, TEAMS_PER_PAGE).then(() => { @@ -131,21 +147,6 @@ export default class SelectTeam extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - const {logout} = this.props.actions; - - switch (event.id) { - case 'close-teams': - this.close(); - break; - case 'logout': - InteractionManager.runAfterInteractions(logout); - break; - } - } - }; - onSelectTeam = async (team) => { this.setState({joining: true}); const {userWithoutTeams} = this.props; diff --git a/app/screens/select_team/select_team.test.js b/app/screens/select_team/select_team.test.js index c722e2f20..19e3d6440 100644 --- a/app/screens/select_team/select_team.test.js +++ b/app/screens/select_team/select_team.test.js @@ -35,15 +35,13 @@ describe('SelectTeam', () => { actions, currentChannelId: 'someId', currentUrl: 'test', - navigator: { - setOnNavigatorEvent: jest.fn(), - }, userWithoutTeams: false, teams: [], theme: Preferences.THEMES.default, teamsRequest: { status: RequestStatus.FAILURE, }, + componentId: 'component-id', }; test('should match snapshot for fail of teams', async () => { diff --git a/app/screens/selector_screen/selector_screen.js b/app/screens/selector_screen/selector_screen.js index 39037bab2..2694641e7 100644 --- a/app/screens/selector_screen/selector_screen.js +++ b/app/screens/selector_screen/selector_screen.js @@ -35,6 +35,7 @@ export default class SelectorScreen extends PureComponent { searchProfiles: PropTypes.func.isRequired, searchChannels: PropTypes.func.isRequired, }), + componentId: PropTypes.string, currentTeamId: PropTypes.string.isRequired, data: PropTypes.arrayOf(PropTypes.object), dataSource: PropTypes.string, @@ -65,8 +66,6 @@ export default class SelectorScreen extends PureComponent { searchResults: [], term: '', }; - - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); } componentDidMount() { @@ -85,7 +84,7 @@ export default class SelectorScreen extends PureComponent { componentDidUpdate(prevProps) { if (this.props.theme !== prevProps.theme) { - setNavigatorStyles(this.props.navigator, this.props.theme); + setNavigatorStyles(this.props.componentId, this.props.theme); } } diff --git a/app/screens/selector_screen/selector_screen.test.js b/app/screens/selector_screen/selector_screen.test.js index a63b9239d..b017809f9 100644 --- a/app/screens/selector_screen/selector_screen.test.js +++ b/app/screens/selector_screen/selector_screen.test.js @@ -72,9 +72,6 @@ describe('SelectorScreen', () => { const baseProps = { actions, currentTeamId: 'someId', - navigator: { - setOnNavigatorEvent: jest.fn(), - }, onSelect: jest.fn(), data: [{text: 'text', value: 'value'}], dataSource: null, diff --git a/app/screens/settings/display_settings/display_settings.js b/app/screens/settings/display_settings/display_settings.js index 02c920e8e..24ffdca47 100644 --- a/app/screens/settings/display_settings/display_settings.js +++ b/app/screens/settings/display_settings/display_settings.js @@ -8,6 +8,7 @@ import { Platform, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import SettingsItem from 'app/screens/settings/settings_item'; import StatusBar from 'app/components/status_bar'; @@ -18,6 +19,7 @@ import ClockDisplay from 'app/screens/clock_display'; export default class DisplaySettings extends PureComponent { static propTypes = { + componentId: PropTypes.string, navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, enableTheme: PropTypes.bool.isRequired, @@ -32,9 +34,14 @@ export default class DisplaySettings extends PureComponent { showClockDisplaySettings: false, }; - constructor(props) { - super(props); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + // TODO: Remove this once styles are passed in push call in + // app/screens/settings/general/settings.js + componentDidAppear() { + setNavigatorStyles(this.props.componentId, this.props.theme); } closeClockDisplaySettings = () => { @@ -100,12 +107,6 @@ export default class DisplaySettings extends PureComponent { }); }); - onNavigatorEvent = (event) => { - if (event.id === 'willAppear') { - setNavigatorStyles(this.props.navigator, this.props.theme); - } - }; - render() { const {theme, enableTimezone, enableTheme} = this.props; const {showClockDisplaySettings} = this.state; diff --git a/app/screens/settings/display_settings/display_settings.test.js b/app/screens/settings/display_settings/display_settings.test.js index 1d1b40804..bd401a916 100644 --- a/app/screens/settings/display_settings/display_settings.test.js +++ b/app/screens/settings/display_settings/display_settings.test.js @@ -17,8 +17,8 @@ describe('DisplaySettings', () => { enableTimezone: false, navigator: { push: jest.fn(), - setOnNavigatorEvent: jest.fn(), }, + componentId: 'component-id', }; test('should match snapshot', () => { diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js index 732672eb1..f1569a2fc 100644 --- a/app/screens/settings/general/settings.js +++ b/app/screens/settings/general/settings.js @@ -11,6 +11,7 @@ import { View, } from 'react-native'; import DeviceInfo from 'react-native-device-info'; +import {Navigation} from 'react-native-navigation'; import SettingsItem from 'app/screens/settings/settings_item'; import StatusBar from 'app/components/status_bar'; @@ -27,6 +28,7 @@ class Settings extends PureComponent { clearErrors: PropTypes.func.isRequired, purgeOfflineStore: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, config: PropTypes.object.isRequired, currentTeamId: PropTypes.string.isRequired, currentUserId: PropTypes.string.isRequired, @@ -43,9 +45,22 @@ class Settings extends PureComponent { joinableTeams: [], }; - constructor(props) { - super(props); - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + // TODO: Remove this once styles are passed in push/showModal call in + // app/components/sidebars/settings/settings_sidebar.js + componentDidAppear() { + setNavigatorStyles(this.props.componentId, this.props.theme); + } + + navigationButtonPressed({buttonId}) { + if (buttonId === 'close-settings') { + this.props.navigator.dismissModal({ + animationType: 'slide-down', + }); + } } errorEmailBody = () => { @@ -107,6 +122,10 @@ class Settings extends PureComponent { goToDisplaySettings = preventDoubleTap(() => { const {intl, navigator, theme} = this.props; + + // TODO: Ensure all styles used in app/utils/theme's setNavigatorStyles + // are passed to DisplaySettings when this push call is updated to RNN v2 + // then remove setNavigatorStyles call in app/screens/settings/display_settings/display_settings.js navigator.push({ screen: 'DisplaySettings', title: intl.formatMessage({id: 'user.settings.modal.display', defaultMessage: 'Display'}), @@ -178,20 +197,6 @@ class Settings extends PureComponent { }); }); - onNavigatorEvent = (event) => { - if (event.id === 'willAppear') { - setNavigatorStyles(this.props.navigator, this.props.theme); - } - - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } - } - }; - openErrorEmail = preventDoubleTap(() => { const {config} = this.props; const recipient = config.SupportEmail; diff --git a/app/screens/settings/notification_settings/notification_settings.js b/app/screens/settings/notification_settings/notification_settings.js index 4e0ddb351..2d269a132 100644 --- a/app/screens/settings/notification_settings/notification_settings.js +++ b/app/screens/settings/notification_settings/notification_settings.js @@ -27,6 +27,7 @@ class NotificationSettings extends PureComponent { actions: PropTypes.shape({ updateMe: PropTypes.func.isRequired, }), + componentId: PropTypes.string, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, navigator: PropTypes.object, @@ -38,7 +39,7 @@ class NotificationSettings extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } const {updateMeRequest, intl} = nextProps; diff --git a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js index 1a20646b8..3f7cbb5cf 100644 --- a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js +++ b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js @@ -7,9 +7,11 @@ import PropTypes from 'prop-types'; import { View, } from 'react-native'; -import {General} from 'mattermost-redux/constants'; +import {Navigation} from 'react-native-navigation'; import {intlShape} from 'react-intl'; +import {General} from 'mattermost-redux/constants'; + import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; @@ -44,8 +46,6 @@ export default class NotificationSettingsAutoResponder extends PureComponent { defaultMessage: 'Hello, I am out of office and unable to respond to messages.', }); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - let autoResponderActive = 'false'; if (props.currentUserStatus === General.OUT_OF_OFFICE && notifyProps.auto_responder_active) { autoResponderActive = 'true'; @@ -58,15 +58,13 @@ export default class NotificationSettingsAutoResponder extends PureComponent { }; } - onNavigatorEvent = (event) => { - if (event.type === 'ScreenChangedEvent') { - switch (event.id) { - case 'willDisappear': - this.saveUserNotifyProps(); - break; - } - } - }; + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + + componentWillUnmount() { + this.saveUserNotifyProps(); + } saveUserNotifyProps = () => { this.props.onBack({ diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js b/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js index f79b5c26d..dc797176d 100644 --- a/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js +++ b/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js @@ -6,7 +6,6 @@ import React from 'react'; import Preferences from 'mattermost-redux/constants/preferences'; import {shallowWithIntl} from 'test/intl-test-helper'; -import {emptyFunction} from 'app/utils/general'; import RadioButtonGroup from 'app/components/radio_button'; @@ -23,7 +22,6 @@ describe('NotificationSettingsEmailAndroid', () => { currentUser: {id: 'current_user_id'}, emailInterval: '30', enableEmailBatching: false, - navigator: {setOnNavigatorEvent: emptyFunction}, actions: { updateMe: jest.fn(), savePreferences: jest.fn(), @@ -31,6 +29,7 @@ describe('NotificationSettingsEmailAndroid', () => { sendEmailNotifications: true, siteName: 'Mattermost', theme: Preferences.THEMES.default, + componentId: 'component-id', }; test('should match snapshot', () => { @@ -154,13 +153,15 @@ describe('NotificationSettingsEmailAndroid', () => { const instance = wrapper.instance(); instance.saveEmailNotifyProps = jest.fn(); - // should not save preference on back button on Android + // Back button on Android should close the modal and trigger + // componentDidDisappear. + // Should not save preference on back button on Android as // saving email preference on Android is done via Save button - instance.onNavigatorEvent({type: 'ScreenChangedEvent', id: 'willDisappear'}); + instance.componentDidDisappear(); expect(instance.saveEmailNotifyProps).toHaveBeenCalledTimes(0); wrapper.setState({newInterval: '0'}); - instance.onNavigatorEvent({type: 'ScreenChangedEvent', id: 'willDisappear'}); + instance.componentDidDisappear(); expect(instance.saveEmailNotifyProps).toHaveBeenCalledTimes(0); }); }); diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js b/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js index dcb068ec4..5c5cee410 100644 --- a/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js +++ b/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js @@ -6,8 +6,6 @@ import {shallow} from 'enzyme'; import Preferences from 'mattermost-redux/constants/preferences'; -import {emptyFunction} from 'app/utils/general'; - import SectionItem from 'app/screens/settings/section_item'; import NotificationSettingsEmailIos from './notification_settings_email.ios.js'; @@ -31,7 +29,6 @@ describe('NotificationSettingsEmailIos', () => { currentUser: {id: 'current_user_id'}, emailInterval: '30', enableEmailBatching: false, - navigator: {setOnNavigatorEvent: emptyFunction}, actions: { updateMe: jest.fn(), savePreferences: jest.fn(), @@ -39,6 +36,7 @@ describe('NotificationSettingsEmailIos', () => { sendEmailNotifications: true, siteName: 'Mattermost', theme: Preferences.THEMES.default, + componentId: 'component-id', }; test('should match snapshot, renderEmailSection', () => { @@ -57,13 +55,13 @@ describe('NotificationSettingsEmailIos', () => { const instance = wrapper.instance(); // should not save preference if email interval has not changed. - instance.onNavigatorEvent({type: 'ScreenChangedEvent', id: 'willDisappear'}); + instance.componentDidDisappear(); expect(baseProps.actions.updateMe).toHaveBeenCalledTimes(0); expect(baseProps.actions.savePreferences).toHaveBeenCalledTimes(0); // should save preference if email interval has changed. wrapper.setState({newInterval: '0'}); - instance.onNavigatorEvent({type: 'ScreenChangedEvent', id: 'willDisappear'}); + instance.componentDidDisappear(); expect(baseProps.actions.updateMe).toHaveBeenCalledTimes(1); expect(baseProps.actions.savePreferences).toHaveBeenCalledTimes(1); }); diff --git a/app/screens/settings/notification_settings_email/notification_settings_email_base.js b/app/screens/settings/notification_settings_email/notification_settings_email_base.js index c63dda1a7..60f56ab8c 100644 --- a/app/screens/settings/notification_settings_email/notification_settings_email_base.js +++ b/app/screens/settings/notification_settings_email/notification_settings_email_base.js @@ -4,6 +4,7 @@ import {PureComponent} from 'react'; import {Platform} from 'react-native'; import PropTypes from 'prop-types'; +import {Navigation} from 'react-native-navigation'; import {Preferences} from 'mattermost-redux/constants'; import {getEmailInterval} from 'mattermost-redux/utils/notify_props'; @@ -17,10 +18,10 @@ export default class NotificationSettingsEmailBase extends PureComponent { savePreferences: PropTypes.func.isRequired, updateMe: PropTypes.func.isRequired, }), + componentId: PropTypes.string, currentUser: PropTypes.object.isRequired, emailInterval: PropTypes.string.isRequired, enableEmailBatching: PropTypes.bool.isRequired, - navigator: PropTypes.object, sendEmailNotifications: PropTypes.bool.isRequired, siteName: PropTypes.string, theme: PropTypes.object.isRequired, @@ -33,7 +34,6 @@ export default class NotificationSettingsEmailBase extends PureComponent { currentUser, emailInterval, enableEmailBatching, - navigator, sendEmailNotifications, } = props; @@ -44,13 +44,15 @@ export default class NotificationSettingsEmailBase extends PureComponent { newInterval: this.computeEmailInterval(notifyProps?.email === 'true' && sendEmailNotifications, enableEmailBatching, emailInterval), showEmailNotificationsModal: false, }; + } - navigator.setOnNavigatorEvent(this.onNavigatorEvent); + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } const { @@ -74,15 +76,11 @@ export default class NotificationSettingsEmailBase extends PureComponent { } } - onNavigatorEvent = (event) => { - if (Platform.OS === 'ios' && event.type === 'ScreenChangedEvent') { - switch (event.id) { - case 'willDisappear': - this.saveEmailNotifyProps(); - break; - } + componentDidDisappear() { + if (Platform.OS === 'ios') { + this.saveEmailNotifyProps(); } - }; + } setEmailInterval = (value) => { this.setState({newInterval: value}); diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js index 79609d5f2..b7a976468 100644 --- a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js +++ b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js @@ -4,12 +4,14 @@ import {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {getNotificationProps} from 'app/utils/notify_props'; import {setNavigatorStyles} from 'app/utils/theme'; export default class NotificationSettingsMentionsBase extends PureComponent { static propTypes = { + componentId: PropTypes.string, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, navigator: PropTypes.object, @@ -27,27 +29,25 @@ export default class NotificationSettingsMentionsBase extends PureComponent { const {currentUser} = props; const notifyProps = getNotificationProps(currentUser); - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - this.goingBack = true; //use to identify if the navigator is popping this screen this.state = this.setStateFromNotifyProps(notifyProps); } + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + } + componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } - onNavigatorEvent = (event) => { - if (event.type === 'ScreenChangedEvent' && this.goingBack) { - switch (event.id) { - case 'willDisappear': - this.saveUserNotifyProps(); - break; - } + componentDidDisappear() { + if (this.goingBack) { + this.saveUserNotifyProps(); } - }; + } setStateFromNotifyProps = (notifyProps) => { const mentionKeys = (notifyProps.mention_keys || '').split(','); diff --git a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js index 4e4f411a1..77e9bd541 100644 --- a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js +++ b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js @@ -3,6 +3,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {ScrollView, View} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; @@ -11,6 +12,7 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class NotificationSettingsMentionsKeywords extends PureComponent { static propTypes = { + componentId: PropTypes.string, keywords: PropTypes.string, navigator: PropTypes.object, onBack: PropTypes.func.isRequired, @@ -23,13 +25,15 @@ export default class NotificationSettingsMentionsKeywords extends PureComponent this.state = { keywords: props.keywords, }; + } - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } @@ -45,15 +49,9 @@ export default class NotificationSettingsMentionsKeywords extends PureComponent return this.setState({keywords}); }; - onNavigatorEvent = (event) => { - if (event.type === 'ScreenChangedEvent') { - switch (event.id) { - case 'willDisappear': - this.props.onBack(this.state.keywords); - break; - } - } - }; + componentDidDisappear() { + this.props.onBack(this.state.keywords); + } render() { const {theme} = this.props; diff --git a/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js b/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js index ad38701ee..15481a7e9 100644 --- a/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js +++ b/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js @@ -5,12 +5,14 @@ import {PureComponent} from 'react'; import {Platform} from 'react-native'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {getNotificationProps} from 'app/utils/notify_props'; import {setNavigatorStyles} from 'app/utils/theme'; export default class NotificationSettingsMobileBase extends PureComponent { static propTypes = { + componentId: PropTypes.string, config: PropTypes.object.isRequired, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, @@ -37,12 +39,15 @@ export default class NotificationSettingsMobileBase extends PureComponent { showMobilePushStatusModal: false, showMobileSoundsModal: false, }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); + } + + componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } @@ -79,15 +84,9 @@ export default class NotificationSettingsMobileBase extends PureComponent { return {}; }; - onNavigatorEvent = (event) => { - if (event.type === 'ScreenChangedEvent') { - switch (event.id) { - case 'willDisappear': - this.saveUserNotifyProps(); - break; - } - } - }; + componentDidDisappear() { + this.saveUserNotifyProps(); + } setMobilePush = (push) => { this.setState({push}); diff --git a/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap b/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap index 81ed4d889..f323d4cbd 100644 --- a/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap +++ b/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap @@ -124,19 +124,6 @@ exports[`TermsOfService should enable/disable navigator buttons on setNavigatorB }, ], }, - "setOnNavigatorEvent": [MockFunction] { - "calls": Array [ - Array [ - [Function], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, } } textStyles={ @@ -376,19 +363,6 @@ exports[`TermsOfService should enable/disable navigator buttons on setNavigatorB }, ], }, - "setOnNavigatorEvent": [MockFunction] { - "calls": Array [ - Array [ - [Function], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, } } textStyles={ @@ -677,19 +651,6 @@ exports[`TermsOfService should match snapshot on enableNavigatorLogout 1`] = ` }, ], }, - "setOnNavigatorEvent": [MockFunction] { - "calls": Array [ - Array [ - [Function], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, } } textStyles={ diff --git a/app/screens/terms_of_service/terms_of_service.js b/app/screens/terms_of_service/terms_of_service.js index f6dd2ba13..850b36e57 100644 --- a/app/screens/terms_of_service/terms_of_service.js +++ b/app/screens/terms_of_service/terms_of_service.js @@ -9,6 +9,7 @@ import { View, } from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import FailedNetworkAction from 'app/components/failed_network_action'; import Loading from 'app/components/loading'; @@ -36,6 +37,7 @@ export default class TermsOfService extends PureComponent { getTermsOfService: PropTypes.func.isRequired, updateMyTermsOfServiceStatus: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, closeButton: PropTypes.object, navigator: PropTypes.object, siteName: PropTypes.string, @@ -72,37 +74,36 @@ export default class TermsOfService extends PureComponent { this.rightButton.title = context.intl.formatMessage({id: 'terms_of_service.agreeButton', defaultMessage: 'I Agree'}); this.leftButton.icon = props.closeButton; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); this.setNavigatorButtons(false); } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + this.getTerms(); } componentDidUpdate(prevProps) { if (this.props.theme !== prevProps.theme) { - setNavigatorStyles(this.props.navigator, this.props.theme); + setNavigatorStyles(this.props.componentId, this.props.theme); } } - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case 'close-terms-of-service': - this.closeTermsAndLogout(); - break; + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case 'close-terms-of-service': + this.closeTermsAndLogout(); + break; - case 'reject-terms-of-service': - this.handleRejectTerms(); - break; + case 'reject-terms-of-service': + this.handleRejectTerms(); + break; - case 'accept-terms-of-service': - this.handleAcceptTerms(); - break; - } + case 'accept-terms-of-service': + this.handleAcceptTerms(); + break; } - }; + } setNavigatorButtons = (enabled = true) => { const buttons = { diff --git a/app/screens/terms_of_service/terms_of_service.test.js b/app/screens/terms_of_service/terms_of_service.test.js index 33a544f55..817dce5d5 100644 --- a/app/screens/terms_of_service/terms_of_service.test.js +++ b/app/screens/terms_of_service/terms_of_service.test.js @@ -31,11 +31,11 @@ describe('TermsOfService', () => { dismissAllModals: jest.fn(), dismissModal: jest.fn(), setButtons: jest.fn(), - setOnNavigatorEvent: jest.fn(), }, theme: Preferences.THEMES.default, closeButton: {}, siteName: 'Mattermost', + componentId: 'component-id', }; test('should match snapshot', () => { diff --git a/app/screens/text_preview/text_preview.js b/app/screens/text_preview/text_preview.js index 532da4efa..68e7b214b 100644 --- a/app/screens/text_preview/text_preview.js +++ b/app/screens/text_preview/text_preview.js @@ -17,6 +17,7 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class TextPreview extends React.PureComponent { static propTypes = { + componentId: PropTypes.string, navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, content: PropTypes.string.isRequired, @@ -24,7 +25,7 @@ export default class TextPreview extends React.PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } diff --git a/app/screens/theme/theme.js b/app/screens/theme/theme.js index a2a37d43f..0fed665d1 100644 --- a/app/screens/theme/theme.js +++ b/app/screens/theme/theme.js @@ -27,11 +27,11 @@ export default class Theme extends React.PureComponent { actions: PropTypes.shape({ savePreferences: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, allowedThemes: PropTypes.arrayOf(PropTypes.object), customTheme: PropTypes.object, isLandscape: PropTypes.bool.isRequired, isTablet: PropTypes.bool.isRequired, - navigator: PropTypes.object.isRequired, teamId: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, userId: PropTypes.string.isRequired, @@ -56,7 +56,7 @@ export default class Theme extends React.PureComponent { componentDidUpdate(prevProps) { if (prevProps.theme !== this.props.theme) { - setNavigatorStyles(this.props.navigator, this.props.theme); + setNavigatorStyles(this.props.componentId, this.props.theme); } } diff --git a/app/screens/theme/theme.test.js b/app/screens/theme/theme.test.js index 3a2651ba5..486b751fe 100644 --- a/app/screens/theme/theme.test.js +++ b/app/screens/theme/theme.test.js @@ -131,9 +131,6 @@ describe('Theme', () => { allowedThemes, isLandscape: false, isTablet: false, - navigator: { - setOnNavigatorEvent: jest.fn(), - }, teamId: 'test-team', theme: Preferences.THEMES.default, userId: 'test-user', diff --git a/app/screens/thread/thread_base.js b/app/screens/thread/thread_base.js index 95718d020..468142ebd 100644 --- a/app/screens/thread/thread_base.js +++ b/app/screens/thread/thread_base.js @@ -17,6 +17,7 @@ export default class ThreadBase extends PureComponent { actions: PropTypes.shape({ selectPost: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, channelId: PropTypes.string.isRequired, channelType: PropTypes.string, displayName: PropTypes.string, @@ -63,7 +64,7 @@ export default class ThreadBase extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } if (this.props.postIds !== nextProps.postIds && !nextProps.postIds.length) { diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index f1bf1ba34..130ffd3a1 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -10,6 +10,7 @@ import { Linking, } from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; @@ -33,6 +34,7 @@ export default class UserProfile extends PureComponent { setChannelDisplayName: PropTypes.func.isRequired, loadBot: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string, config: PropTypes.object.isRequired, currentDisplayName: PropTypes.string, navigator: PropTypes.object, @@ -65,23 +67,35 @@ export default class UserProfile extends PureComponent { rightButtons: [this.rightButton], }; - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); props.navigator.setButtons(buttons); } } componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); + setNavigatorStyles(this.props.componentId, nextProps.theme); } } componentDidMount() { + this.navigationEventListener = Navigation.events().bindComponent(this); + if (this.props.user && this.props.user.is_bot) { this.props.actions.loadBot(this.props.user.id); } } + navigationButtonPressed({buttonId}) { + switch (buttonId) { + case this.rightButton.id: + this.goToEditProfile(); + break; + case 'close-settings': + this.close(); + break; + } + } + close = () => { const {navigator, theme} = this.props; @@ -241,19 +255,6 @@ export default class UserProfile extends PureComponent { }); }; - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - switch (event.id) { - case this.rightButton.id: - this.goToEditProfile(); - break; - case 'close-settings': - this.close(); - break; - } - } - }; - renderAdditionalOptions = () => { if (!Config.ExperimentalProfileLinks) { return null; diff --git a/app/screens/user_profile/user_profile.test.js b/app/screens/user_profile/user_profile.test.js index e1dac798e..af4e8d903 100644 --- a/app/screens/user_profile/user_profile.test.js +++ b/app/screens/user_profile/user_profile.test.js @@ -39,6 +39,7 @@ describe('user_profile', () => { enableTimezone: false, militaryTime: false, isMyUser: false, + componentId: 'component-id', }; const user = { @@ -126,8 +127,8 @@ describe('user_profile', () => { {context: {intl: {formatMessage: jest.fn()}}}, ); - const event = {type: 'NavBarButtonPress', id: wrapper.instance().rightButton.id}; - wrapper.instance().onNavigatorEvent(event); + const event = {buttonId: wrapper.instance().rightButton.id}; + wrapper.instance().navigationButtonPressed(event); setTimeout(() => { expect(props.navigator.push).toHaveBeenCalledTimes(1); }, 0); @@ -144,13 +145,13 @@ describe('user_profile', () => { {context: {intl: {formatMessage: jest.fn()}}}, ); - const event = {type: 'NavBarButtonPress', id: 'close-settings'}; - wrapper.instance().onNavigatorEvent(event); + const event = {buttonId: 'close-settings'}; + wrapper.instance().navigationButtonPressed(event); expect(props.navigator.dismissModal).toHaveBeenCalledTimes(1); props.fromSettings = false; wrapper.setProps({...props}); - wrapper.instance().onNavigatorEvent(event); + wrapper.instance().navigationButtonPressed(event); expect(props.navigator.resetTo).toHaveBeenCalledTimes(1); }); }); diff --git a/app/utils/theme.js b/app/utils/theme.js index f307f6398..c8c751371 100644 --- a/app/utils/theme.js +++ b/app/utils/theme.js @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import {StyleSheet} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import * as ThemeUtils from 'mattermost-redux/utils/theme_utils'; @@ -19,12 +20,21 @@ export function concatStyles(...styles) { return [].concat(styles); } -export function setNavigatorStyles(navigator, theme) { - navigator.setStyle({ - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, +export function setNavigatorStyles(componentId, theme) { + Navigation.mergeOptions(componentId, { + topBar: { + title: { + color: theme.sidebarHeaderTextColor, + }, + background: { + color: theme.sidebarHeaderBg, + }, + leftButtonColor: theme.sidebarHeaderTextColor, + rightButtonColor: theme.sidebarHeaderTextColor, + }, + layout: { + backgroundColor: theme.centerChannelBg, + }, }); } diff --git a/package-lock.json b/package-lock.json index 622671a11..e688da4d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5238,6 +5238,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -5282,7 +5283,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "4.0.1", @@ -7662,25 +7664,25 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, + "resolved": "", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, + "resolved": "", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true }, "aproba": { "version": "1.2.0", - "resolved": false, + "resolved": "", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": false, + "resolved": "", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { @@ -7690,14 +7692,15 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, + "resolved": "", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7705,37 +7708,37 @@ }, "chownr": { "version": "1.1.1", - "resolved": false, + "resolved": "", "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, + "resolved": "", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, + "resolved": "", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "4.1.1", - "resolved": false, + "resolved": "", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "optional": true, "requires": { @@ -7744,25 +7747,25 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": false, + "resolved": "", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, + "resolved": "", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": false, + "resolved": "", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "optional": true, "requires": { @@ -7771,13 +7774,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, + "resolved": "", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { @@ -7793,7 +7796,7 @@ }, "glob": { "version": "7.1.3", - "resolved": false, + "resolved": "", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "optional": true, "requires": { @@ -7807,13 +7810,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": false, + "resolved": "", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "optional": true, "requires": { @@ -7822,7 +7825,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": false, + "resolved": "", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "optional": true, "requires": { @@ -7831,7 +7834,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { @@ -7841,48 +7844,51 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "optional": true }, "ini": { "version": "1.3.5", - "resolved": false, + "resolved": "", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, + "resolved": "", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "optional": true }, "minipass": { "version": "2.3.5", - "resolved": false, + "resolved": "", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7890,7 +7896,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": false, + "resolved": "", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "optional": true, "requires": { @@ -7899,21 +7905,22 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.1.1", - "resolved": false, + "resolved": "", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "optional": true }, "needle": { "version": "2.3.0", - "resolved": false, + "resolved": "", "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "optional": true, "requires": { @@ -7924,7 +7931,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": false, + "resolved": "", "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "optional": true, "requires": { @@ -7942,7 +7949,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "optional": true, "requires": { @@ -7952,13 +7959,13 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": false, + "resolved": "", "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": false, + "resolved": "", "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "optional": true, "requires": { @@ -7968,7 +7975,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, + "resolved": "", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { @@ -7980,39 +7987,40 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": false, + "resolved": "", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, + "resolved": "", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { @@ -8022,19 +8030,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "optional": true }, "rc": { "version": "1.2.8", - "resolved": false, + "resolved": "", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { @@ -8046,7 +8054,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "optional": true } @@ -8054,7 +8062,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "optional": true, "requires": { @@ -8069,7 +8077,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": false, + "resolved": "", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "optional": true, "requires": { @@ -8078,44 +8086,45 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, + "resolved": "", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, + "resolved": "", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.7.0", - "resolved": false, + "resolved": "", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8124,7 +8133,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { @@ -8133,21 +8142,22 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, + "resolved": "", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.8", - "resolved": false, + "resolved": "", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "optional": true, "requires": { @@ -8162,13 +8172,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": false, + "resolved": "", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { @@ -8177,13 +8187,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true }, "yallist": { "version": "3.0.3", - "resolved": false, + "resolved": "", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "optional": true } @@ -14170,7 +14180,7 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, @@ -14182,7 +14192,7 @@ }, "cliui": { "version": "4.1.0", - "resolved": false, + "resolved": "", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { @@ -14211,7 +14221,7 @@ }, "find-up": { "version": "3.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { @@ -14226,13 +14236,13 @@ }, "invert-kv": { "version": "2.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "lcid": { "version": "2.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { @@ -14241,7 +14251,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { @@ -14268,7 +14278,7 @@ }, "os-locale": { "version": "3.1.0", - "resolved": false, + "resolved": "", "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { @@ -14288,7 +14298,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { @@ -14309,7 +14319,7 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, @@ -14343,7 +14353,7 @@ }, "strip-ansi": { "version": "4.0.0", - "resolved": false, + "resolved": "", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { @@ -14352,13 +14362,13 @@ }, "uuid": { "version": "3.3.2", - "resolved": false, + "resolved": "", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "y18n": { "version": "4.0.0", - "resolved": false, + "resolved": "", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, @@ -16045,7 +16055,8 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "2.3.2", @@ -16308,7 +16319,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "dev": true, + "optional": true }, "micromatch": { "version": "3.1.10", From 50a738a702863e3f6511f0ff9c0fc75228298de6 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Wed, 19 Jun 2019 12:42:27 -0700 Subject: [PATCH 07/19] [MM-16009] Update screens that can be displayed prior to the Channel screen. (#2887) * Update screens * Update login tests * Remove done * Fix failing tests --- app/actions/navigation.js | 39 ++++ .../channel_members/channel_members.test.js | 1 + app/screens/client_upgrade/client_upgrade.js | 23 +- app/screens/login/index.js | 3 +- app/screens/login/login.js | 42 +--- app/screens/login/login.test.js | 34 ++- app/screens/login_options/index.js | 13 +- app/screens/login_options/login_options.js | 56 +---- app/screens/mfa/mfa.js | 5 +- app/screens/select_server/index.js | 5 +- app/screens/select_server/select_server.js | 73 +++--- app/screens/sso/index.js | 2 + app/screens/sso/sso.js | 18 +- package-lock.json | 212 ++++++++---------- package.json | 1 + 15 files changed, 256 insertions(+), 271 deletions(-) diff --git a/app/actions/navigation.js b/app/actions/navigation.js index 32702f58b..3cb3e7dc0 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -3,6 +3,8 @@ import {Navigation} from 'react-native-navigation'; +import merge from 'deepmerge'; + import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; export function resetToChannel() { @@ -16,6 +18,9 @@ export function resetToChannel() { component: { name: 'Channel', options: { + layout: { + backgroundColor: 'transparent', + }, statusBar: { visible: true, }, @@ -78,3 +83,37 @@ export function resetToSelectServer(allowOtherServers) { }); }; } + +export function goToScreen(componentId, name, title, passProps = {}, options = {}) { + return (dispatch, getState) => { + const theme = getTheme(getState()); + const defaultOptions = { + layout: { + backgroundColor: theme.centerChannelBg, + }, + topBar: { + animate: true, + visible: true, + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + title: { + color: theme.sidebarHeaderTextColor, + text: title, + }, + }, + }; + + Navigation.push(componentId, { + component: { + name, + passProps, + options: merge(defaultOptions, options), + }, + }); + }; +} \ No newline at end of file diff --git a/app/screens/channel_members/channel_members.test.js b/app/screens/channel_members/channel_members.test.js index 16da3a7d5..efb4e750e 100644 --- a/app/screens/channel_members/channel_members.test.js +++ b/app/screens/channel_members/channel_members.test.js @@ -26,6 +26,7 @@ describe('ChannelMembers', () => { searchProfiles: jest.fn(), }, navigator, + componentId: 'component-id', }; test('should match snapshot', () => { diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js index 9ce684d33..a57dccac1 100644 --- a/app/screens/client_upgrade/client_upgrade.js +++ b/app/screens/client_upgrade/client_upgrade.js @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; +import {Navigation} from 'react-native-navigation'; import PropTypes from 'prop-types'; import { Alert, @@ -12,7 +13,6 @@ import { View, } from 'react-native'; import {intlShape} from 'react-intl'; -import {Navigation} from 'react-native-navigation'; import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; @@ -34,7 +34,6 @@ export default class ClientUpgrade extends PureComponent { downloadLink: PropTypes.string.isRequired, forceUpgrade: PropTypes.bool, latestVersion: PropTypes.string, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, upgradeType: PropTypes.string, }; @@ -67,9 +66,7 @@ export default class ClientUpgrade extends PureComponent { navigationButtonPressed({buttonId}) { if (buttonId === 'close-upgrade') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + Navigation.dismissModal(this.props.componentId); } } @@ -95,12 +92,18 @@ export default class ClientUpgrade extends PureComponent { } handleClose = () => { - if (this.props.closeAction) { - this.props.closeAction(); - } else if (this.props.userCheckedForUpgrade) { - this.props.navigator.pop(); + const { + closeAction, + userCheckedForUpgrade, + componentId, + } = this.props; + + if (closeAction) { + closeAction(); + } else if (userCheckedForUpgrade) { + Navigation.pop(componentId); } else { - this.props.navigator.dismissModal(); + Navigation.dismissModal(componentId); } }; diff --git a/app/screens/login/index.js b/app/screens/login/index.js index 22e7a1956..5ec265094 100644 --- a/app/screens/login/index.js +++ b/app/screens/login/index.js @@ -8,7 +8,7 @@ import {login} from 'mattermost-redux/actions/users'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {resetToChannel} from 'app/actions/navigation'; +import {resetToChannel, goToScreen} from 'app/actions/navigation'; import LoginActions from 'app/actions/views/login'; import Login from './login.js'; @@ -32,6 +32,7 @@ function mapDispatchToProps(dispatch) { ...LoginActions, login, resetToChannel, + goToScreen, }, dispatch), }; } diff --git a/app/screens/login/login.js b/app/screens/login/login.js index 672e1276b..047aaed56 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -32,7 +32,7 @@ import telemetry from 'app/telemetry'; import {RequestStatus} from 'mattermost-redux/constants'; -const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error']; +export const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error']; export default class Login extends PureComponent { static propTypes = { @@ -43,8 +43,9 @@ export default class Login extends PureComponent { scheduleExpiredNotification: PropTypes.func.isRequired, login: PropTypes.func.isRequired, resetToChannel: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, - navigator: PropTypes.object.isRequired, // TODO remove me + componentId: PropTypes.string.isRequired, theme: PropTypes.object, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, @@ -94,23 +95,12 @@ export default class Login extends PureComponent { }; goToMfa = () => { + const {componentId, actions} = this.props; const {intl} = this.context; - const {navigator, theme} = this.props; + const screen = 'MFA'; + const title = intl.formatMessage({id: 'mobile.routes.mfa', defaultMessage: 'Multi-factor Authentication'}); - this.setState({isLoading: false}); - - navigator.push({ - screen: 'MFA', - title: intl.formatMessage({id: 'mobile.routes.mfa', defaultMessage: 'Multi-factor Authentication'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(componentId, screen, title); }; blur = () => { @@ -297,20 +287,12 @@ export default class Login extends PureComponent { }; forgotPassword = () => { + const {actions, componentId} = this.props; const {intl} = this.context; - const {navigator, theme} = this.props; - navigator.push({ - screen: 'ForgotPassword', - title: intl.formatMessage({id: 'password_form.title', defaultMessage: 'Password Reset'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const screen = 'ForgotPassword'; + const title = intl.formatMessage({id: 'password_form.title', defaultMessage: 'Password Reset'}); + + actions.goToScreen(componentId, screen, title); } render() { diff --git a/app/screens/login/login.test.js b/app/screens/login/login.test.js index 3b345c187..040221c12 100644 --- a/app/screens/login/login.test.js +++ b/app/screens/login/login.test.js @@ -9,6 +9,7 @@ import FormattedText from 'app/components/formatted_text'; import {shallowWithIntl} from 'test/intl-test-helper'; +import {mfaExpectedErrors} from 'app/screens/login/login'; import Login from './login'; describe('Login', () => { @@ -23,7 +24,7 @@ describe('Login', () => { loginId: '', password: '', loginRequest: {}, - navigator: {}, + componentId: 'component-id', actions: { handleLoginIdChanged: jest.fn(), handlePasswordChanged: jest.fn(), @@ -31,6 +32,7 @@ describe('Login', () => { scheduleExpiredNotification: jest.fn(), login: jest.fn(), resetToChannel: jest.fn(), + goToScreen: jest.fn(), }, }; @@ -114,4 +116,34 @@ describe('Login', () => { // This test times out if resetToChannel hasn't been called }); + + test('should go to MFA screen when login response returns MFA error', () => { + const mfaError = { + error: { + server_error_id: mfaExpectedErrors[0], + }, + }; + + const wrapper = shallowWithIntl(); + wrapper.instance().checkLoginResponse(mfaError); + + expect(baseProps.actions.goToScreen). + toHaveBeenCalledWith( + baseProps.componentId, + 'MFA', + 'Multi-factor Authentication', + ); + }); + + test('should go to ForgotPassword screen when forgotPassword is called', () => { + const wrapper = shallowWithIntl(); + wrapper.instance().forgotPassword(); + + expect(baseProps.actions.goToScreen). + toHaveBeenCalledWith( + baseProps.componentId, + 'ForgotPassword', + 'Password Reset', + ); + }); }); diff --git a/app/screens/login_options/index.js b/app/screens/login_options/index.js index bb885e5bd..6df348adf 100644 --- a/app/screens/login_options/index.js +++ b/app/screens/login_options/index.js @@ -1,8 +1,11 @@ // 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 {goToScreen} from 'app/actions/navigation'; + import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; @@ -18,4 +21,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(LoginOptions); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(LoginOptions); diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js index f0b05522d..8d4425bc0 100644 --- a/app/screens/login_options/login_options.js +++ b/app/screens/login_options/login_options.js @@ -12,7 +12,6 @@ import { Text, } from 'react-native'; import Button from 'react-native-button'; -import {Navigation} from 'react-native-navigation'; import {ViewTypes} from 'app/constants'; import FormattedText from 'app/components/formatted_text'; @@ -26,11 +25,12 @@ import logo from 'assets/images/logo.png'; export default class LoginOptions extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, // TODO remove me config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, - theme: PropTypes.object, }; static contextTypes = { @@ -46,53 +46,21 @@ export default class LoginOptions extends PureComponent { } goToLogin = preventDoubleTap(() => { + const {actions, componentId} = this.props; const {intl} = this.context; - const {componentId, theme} = this.props; + const screen = 'Login'; + const title = intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'}); - Navigation.push(componentId, { - component: { - name: 'Login', - passProps: { - theme, - }, - options: { - topBar: { - backButton: { - color: theme.sidebarHeaderTextColor, - title: '', - }, - background: { - color: theme.sidebarHeaderBg, - }, - title: { - color: theme.sidebarHeaderTextColor, - text: intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'}), - }, - visible: true, - }, - }, - }, - }); + actions.goToScreen(componentId, screen, title); }); goToSSO = (ssoType) => { + const {actions, componentId} = this.props; const {intl} = this.context; - const {navigator, theme} = this.props; - navigator.push({ - screen: 'SSO', - title: intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - ssoType, - }, - }); + const screen = 'SSO'; + const title = intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'}); + + actions.goToScreen(componentId, screen, title, {ssoType}); }; orientationDidChange = () => { diff --git a/app/screens/mfa/mfa.js b/app/screens/mfa/mfa.js index 1d7c64b70..81ef5f6f5 100644 --- a/app/screens/mfa/mfa.js +++ b/app/screens/mfa/mfa.js @@ -14,6 +14,7 @@ import { View, } from 'react-native'; import Button from 'react-native-button'; +import {Navigation} from 'react-native-navigation'; import {RequestStatus} from 'mattermost-redux/constants'; @@ -28,10 +29,10 @@ import {setMfaPreflightDone} from 'app/utils/security'; export default class Mfa extends PureComponent { static propTypes = { - navigator: PropTypes.object, actions: PropTypes.shape({ login: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string.isRequired, loginId: PropTypes.string.isRequired, password: PropTypes.string.isRequired, loginRequest: PropTypes.object.isRequired, @@ -56,7 +57,7 @@ export default class Mfa extends PureComponent { // In case the login is successful the previous scene (login) will take care of the transition if (this.props.loginRequest.status === RequestStatus.STARTED && nextProps.loginRequest.status === RequestStatus.FAILURE) { - this.props.navigator.pop({animated: true}); + Navigation.pop(this.props.componentId); } } diff --git a/app/screens/select_server/index.js b/app/screens/select_server/index.js index 5a7c593e8..8bc27e0cf 100644 --- a/app/screens/select_server/index.js +++ b/app/screens/select_server/index.js @@ -6,10 +6,9 @@ import {connect} from 'react-redux'; import {getPing, resetPing, setServerVersion} from 'mattermost-redux/actions/general'; import {login} from 'mattermost-redux/actions/users'; -import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {resetToChannel} from 'app/actions/navigation'; +import {resetToChannel, goToScreen} from 'app/actions/navigation'; import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade'; import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {loadConfigAndLicense} from 'app/actions/views/root'; @@ -31,7 +30,6 @@ function mapStateToProps(state) { latestVersion, license, minVersion, - theme: getTheme(state), }; } @@ -48,6 +46,7 @@ function mapDispatchToProps(dispatch) { setLastUpgradeCheck, setServerVersion, resetToChannel, + goToScreen, }, dispatch), }; } diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index 91e9458e1..16cb8e4b6 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -22,6 +22,8 @@ import { import Button from 'react-native-button'; import RNFetchBlob from 'rn-fetch-blob'; +import merge from 'deepmerge'; + import {Client4} from 'mattermost-redux/client'; import ErrorText from 'app/components/error_text'; @@ -61,9 +63,7 @@ export default class SelectServer extends PureComponent { latestVersion: PropTypes.string, license: PropTypes.object, minVersion: PropTypes.string, - navigator: PropTypes.object.isRequired, // TODO remove me serverUrl: PropTypes.string.isRequired, - theme: PropTypes.object, }; static contextTypes = { @@ -101,8 +101,6 @@ export default class SelectServer extends PureComponent { telemetry.end(['start:select_server_screen']); telemetry.save(); - - this.navigationEventListener = Navigation.events().bindComponent(this); } componentWillUpdate(nextProps, nextState) { @@ -159,32 +157,18 @@ export default class SelectServer extends PureComponent { return stripTrailingSlashes(preUrl.protocol + '//' + preUrl.host + preUrl.pathname); }; - goToNextScreen = (screen, title) => { - const {componentId, theme} = this.props; - - Navigation.push(componentId, { - component: { - name: screen, - options: { - popGesture: !LocalConfig.AutoSelectServerUrl, - topBar: { - backButton: { - color: theme.sidebarHeaderTextColor, - title: '', - }, - background: { - color: theme.sidebarHeaderBg, - }, - title: { - color: theme.sidebarHeaderTextColor, - text: title, - }, - visible: !LocalConfig.AutoSelectServerUrl, - height: LocalConfig.AutoSelectServerUrl ? 0 : null, - }, - }, + goToNextScreen = (screen, title, passProps = {}, navOptions = {}) => { + const {actions, componentId} = this.props; + const defaultOptions = { + popGesture: !LocalConfig.AutoSelectServerUrl, + topBar: { + visible: !LocalConfig.AutoSelectServerUrl, + height: LocalConfig.AutoSelectServerUrl ? 0 : null, }, - }); + }; + const options = merge(defaultOptions, navOptions); + + actions.goToScreen(componentId, screen, title, passProps, options); }; handleAndroidKeyboard = () => { @@ -272,26 +256,19 @@ export default class SelectServer extends PureComponent { handleShowClientUpgrade = (upgradeType) => { const {formatMessage} = this.context.intl; - const {theme} = this.props; + const screen = 'ClientUpgrade'; + const title = formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Client Upgrade'}); + const passProps = { + closeAction: this.handleLoginOptions, + upgradeType, + }; + const options = { + statusBar: { + visible: false, + }, + }; - this.props.navigator.push({ - screen: 'ClientUpgrade', - title: formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Client Upgrade'}), - backButtonTitle: '', - navigatorStyle: { - navBarHidden: LocalConfig.AutoSelectServerUrl, - disabledBackGesture: LocalConfig.AutoSelectServerUrl, - statusBarHidden: true, - statusBarHideWithNavBar: true, - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - }, - passProps: { - closeAction: this.handleLoginOptions, - upgradeType, - }, - }); + this.goToNextScreen(screen, title, passProps, options); }; handleTextChanged = (url) => { diff --git a/app/screens/sso/index.js b/app/screens/sso/index.js index f17246b5b..64553978a 100644 --- a/app/screens/sso/index.js +++ b/app/screens/sso/index.js @@ -4,6 +4,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; +import {resetToChannel} from 'app/actions/navigation'; import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; @@ -24,6 +25,7 @@ function mapDispatchToProps(dispatch) { scheduleExpiredNotification, handleSuccessfulLogin, setStoreFromLocalData, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/sso/sso.js b/app/screens/sso/sso.js index 3538d5e73..1ae96be25 100644 --- a/app/screens/sso/sso.js +++ b/app/screens/sso/sso.js @@ -61,7 +61,6 @@ const oneLoginFormScalingJS = ` class SSO extends PureComponent { static propTypes = { intl: intlShape.isRequired, - navigator: PropTypes.object, theme: PropTypes.object, serverUrl: PropTypes.string.isRequired, ssoType: PropTypes.string.isRequired, @@ -69,6 +68,7 @@ class SSO extends PureComponent { scheduleExpiredNotification: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, setStoreFromLocalData: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, }; @@ -115,25 +115,11 @@ class SSO extends PureComponent { }; goToChannel = () => { - const {navigator} = this.props; tracker.initialLoad = Date.now(); this.scheduleSessionExpiredNotification(); - navigator.resetTo({ - screen: 'Channel', - title: '', - animated: false, - backButtonTitle: '', - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - }, - }); + this.props.actions.resetToChannel(); }; onMessage = (event) => { diff --git a/package-lock.json b/package-lock.json index ed4c67ff7..c2c6bbba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5238,7 +5238,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -5283,8 +5282,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "optional": true + "dev": true }, "is-glob": { "version": "4.0.1", @@ -5795,6 +5793,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.1.tgz", + "integrity": "sha512-+hbDSzTqEW0fWgnlKksg7XAOtT+ddZS5lHZJ6f6MdixRs9wQy+50fm1uUCVb1IkvjLUYX/SfFO021ZNwriURTw==" + }, "default-require-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", @@ -7664,25 +7667,24 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { @@ -7692,15 +7694,13 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7708,37 +7708,34 @@ }, "chownr": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "resolved": "", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "resolved": "", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "optional": true, "requires": { @@ -7747,25 +7744,25 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "optional": true, "requires": { @@ -7774,13 +7771,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { @@ -7796,7 +7793,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "optional": true, "requires": { @@ -7810,13 +7807,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "optional": true, "requires": { @@ -7825,7 +7822,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "optional": true, "requires": { @@ -7834,7 +7831,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { @@ -7844,51 +7841,46 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": "", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7896,7 +7888,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "optional": true, "requires": { @@ -7905,22 +7897,21 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "optional": true }, "needle": { "version": "2.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "optional": true, "requires": { @@ -7931,7 +7922,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "optional": true, "requires": { @@ -7949,7 +7940,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "optional": true, "requires": { @@ -7959,13 +7950,13 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "optional": true, "requires": { @@ -7975,7 +7966,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { @@ -7987,40 +7978,38 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { @@ -8030,19 +8019,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { @@ -8054,7 +8043,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "optional": true } @@ -8062,7 +8051,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "optional": true, "requires": { @@ -8077,7 +8066,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "optional": true, "requires": { @@ -8086,45 +8075,43 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.7.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8133,7 +8120,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { @@ -8142,22 +8129,21 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "optional": true, "requires": { @@ -8172,13 +8158,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { @@ -8187,15 +8173,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "yallist": { "version": "3.0.3", - "resolved": "", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "optional": true + "resolved": false, + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } }, @@ -14181,7 +14165,7 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, @@ -14193,7 +14177,7 @@ }, "cliui": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { @@ -14222,7 +14206,7 @@ }, "find-up": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { @@ -14237,13 +14221,13 @@ }, "invert-kv": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "lcid": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { @@ -14252,7 +14236,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { @@ -14279,7 +14263,7 @@ }, "os-locale": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { @@ -14299,7 +14283,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { @@ -14320,7 +14304,7 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, @@ -14354,7 +14338,7 @@ }, "strip-ansi": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { @@ -14363,13 +14347,13 @@ }, "uuid": { "version": "3.3.2", - "resolved": "", + "resolved": false, "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "y18n": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, @@ -16051,8 +16035,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true + "dev": true }, "braces": { "version": "2.3.2", @@ -16315,8 +16298,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true + "dev": true }, "micromatch": { "version": "3.1.10", diff --git a/package.json b/package.json index 7cdf99eb3..36a79f431 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "commonmark": "github:mattermost/commonmark.js#4224e725f14b5d4e340a62d7e56d5ad451e9f8cf", "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#3a2ac19cab725ad28b170fdc1d397dddedcf87eb", "deep-equal": "1.0.1", + "deepmerge": "3.2.1", "emoji-regex": "8.0.0", "fuse.js": "3.4.4", "intl": "1.2.5", From 913f05e131dc3532d4143da4d70ed1a04715f018 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Mon, 24 Jun 2019 10:58:15 -0700 Subject: [PATCH 08/19] [MM-16011] Update screens related to main sidebar (#2907) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments --- app/actions/navigation.js | 163 +++++++++++++++++- app/actions/views/search.js | 2 + .../profile_picture_button.test.js.snap | 10 +- .../attachment_button.test.js.snap | 0 .../attachment_button.js | 20 +-- .../attachment_button.test.js | 3 + app/components/attachment_button/index.js | 19 ++ app/components/root/index.js | 16 +- app/components/root/root.js | 50 ++---- .../safe_area_view/safe_area_view.ios.js | 11 -- .../__snapshots__/channel_item.test.js.snap | 63 +++---- .../channel_item/channel_item.js | 52 +++--- .../channel_item/channel_item.test.js | 4 +- .../main/channels_list/channels_list.js | 7 +- .../filtered_list/filtered_list.js | 2 + .../sidebars/main/channels_list/list/index.js | 13 +- .../sidebars/main/channels_list/list/list.js | 154 +++++++---------- app/components/sidebars/main/main_sidebar.js | 8 +- .../sidebars/main/teams_list/index.js | 2 + .../sidebars/main/teams_list/teams_list.js | 34 ++-- app/screens/channel/channel.ios.js | 11 +- app/screens/channel/channel_base.js | 4 +- .../channel_search_button.js | 4 +- .../channel_search_button/index.js | 2 +- app/screens/channel/index.js | 2 + app/screens/channel_peek/channel_peek.js | 5 +- app/screens/index.js | 1 + app/screens/more_channels/more_channels.js | 21 ++- app/screens/more_dms/more_dms.js | 22 ++- app/screens/options_modal/options_modal.js | 16 +- app/screens/search/search.js | 8 +- app/screens/select_team/index.js | 6 +- app/screens/select_team/select_team.js | 24 +-- app/screens/select_team/select_team.test.js | 1 + ios/Mattermost/AppDelegate.m | 2 - package-lock.json | 6 +- package.json | 2 +- 37 files changed, 455 insertions(+), 315 deletions(-) rename app/components/{ => attachment_button}/__snapshots__/attachment_button.test.js.snap (100%) rename app/components/{ => attachment_button}/attachment_button.js (97%) rename app/components/{ => attachment_button}/attachment_button.test.js (96%) create mode 100644 app/components/attachment_button/index.js diff --git a/app/actions/navigation.js b/app/actions/navigation.js index 3cb3e7dc0..88880e01b 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -1,13 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {Platform} from 'react-native'; import {Navigation} from 'react-native-navigation'; import merge from 'deepmerge'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; -export function resetToChannel() { +export function resetToChannel(passProps = {}) { return (dispatch, getState) => { const theme = getTheme(getState()); @@ -17,6 +18,7 @@ export function resetToChannel() { children: [{ component: { name: 'Channel', + passProps, options: { layout: { backgroundColor: 'transparent', @@ -25,6 +27,8 @@ export function resetToChannel() { visible: true, }, topBar: { + visible: false, + height: 0, backButton: { color: theme.sidebarHeaderTextColor, title: '', @@ -35,8 +39,6 @@ export function resetToChannel() { title: { color: theme.sidebarHeaderTextColor, }, - visible: false, - height: 0, }, }, }, @@ -84,6 +86,48 @@ export function resetToSelectServer(allowOtherServers) { }; } +export function resetToTeams(name, title, passProps = {}, options = {}) { + return (dispatch, getState) => { + const theme = getTheme(getState()); + const defaultOptions = { + layout: { + backgroundColor: theme.centerChannelBg, + }, + statusBar: { + visible: true, + }, + topBar: { + visible: true, + title: { + color: theme.sidebarHeaderTextColor, + text: title, + }, + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + }, + }; + + Navigation.setRoot({ + root: { + stack: { + children: [{ + component: { + name, + passProps, + options: merge(defaultOptions, options), + }, + }], + }, + }, + }); + }; +} + export function goToScreen(componentId, name, title, passProps = {}, options = {}) { return (dispatch, getState) => { const theme = getTheme(getState()); @@ -108,6 +152,119 @@ export function goToScreen(componentId, name, title, passProps = {}, options = { }, }; + Navigation.push(componentId, { + component: { + name, + passProps, + options: merge(defaultOptions, options), + }, + }); + }; +} + +export function showModal(name, title, passProps = {}, options = {}) { + return (dispatch, getState) => { + const theme = getTheme(getState()); + const defaultOptions = { + layout: { + backgroundColor: theme.centerChannelBg, + }, + statusBar: { + visible: true, + }, + topBar: { + animate: true, + visible: true, + backButton: { + color: theme.sidebarHeaderTextColor, + title: '', + }, + background: { + color: theme.sidebarHeaderBg, + }, + title: { + color: theme.sidebarHeaderTextColor, + text: title, + }, + }, + }; + + Navigation.showModal({ + stack: { + children: [{ + component: { + name, + passProps, + options: merge(defaultOptions, options), + }, + }], + }, + }); + }; +} + +export function showModalOverCurrentContext(name, passProps = {}, options = {}) { + return (dispatch) => { + const title = ''; + const animationsEnabled = (Platform.OS === 'android').toString(); + const defaultOptions = { + modalPresentationStyle: 'overCurrentContext', + layout: { + backgroundColor: 'transparent', + }, + topBar: { + visible: false, + height: 0, + }, + animations: { + showModal: { + enabled: animationsEnabled, + alpha: { + from: 0, + to: 1, + duration: 250, + }, + }, + dismissModal: { + enabled: animationsEnabled, + alpha: { + from: 1, + to: 0, + duration: 250, + }, + }, + }, + }; + const mergeOptions = merge(defaultOptions, options); + + dispatch(showModal(name, title, passProps, mergeOptions)); + }; +} + +export function showSearchModal(initialValue = '') { + return (dispatch) => { + const name = 'Search'; + const title = ''; + const passProps = {initialValue}; + const options = { + topBar: { + visible: false, + height: 0, + }, + }; + + dispatch(showModal(name, title, passProps, options)); + }; +} + +export function peek(componentId, name, passProps = {}, options = {}) { + return () => { + const defaultOptions = { + preview: { + commit: false, + }, + }; + Navigation.push(componentId, { component: { name, diff --git a/app/actions/views/search.js b/app/actions/views/search.js index 2726e6635..0e6f0c3a2 100644 --- a/app/actions/views/search.js +++ b/app/actions/views/search.js @@ -14,6 +14,8 @@ export function handleSearchDraftChanged(text) { }; } +// TODO: Remove this once all screens/components call +// app/actions/navigation's showSearchModal export function showSearchModal(navigator, initialValue = '') { return (dispatch, getState) => { const theme = getTheme(getState()); diff --git a/app/components/__snapshots__/profile_picture_button.test.js.snap b/app/components/__snapshots__/profile_picture_button.test.js.snap index c93aadecd..97f0ea52d 100644 --- a/app/components/__snapshots__/profile_picture_button.test.js.snap +++ b/app/components/__snapshots__/profile_picture_button.test.js.snap @@ -1,20 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`profile_picture_button should match snapshot 1`] = ` - `; diff --git a/app/components/__snapshots__/attachment_button.test.js.snap b/app/components/attachment_button/__snapshots__/attachment_button.test.js.snap similarity index 100% rename from app/components/__snapshots__/attachment_button.test.js.snap rename to app/components/attachment_button/__snapshots__/attachment_button.test.js.snap diff --git a/app/components/attachment_button.js b/app/components/attachment_button/attachment_button.js similarity index 97% rename from app/components/attachment_button.js rename to app/components/attachment_button/attachment_button.js index 29bd3d0d0..5c746c5d8 100644 --- a/app/components/attachment_button.js +++ b/app/components/attachment_button/attachment_button.js @@ -27,6 +27,9 @@ const ShareExtension = NativeModules.MattermostShare; export default class AttachmentButton extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + showModalOverCurrentContext: PropTypes.func.isRequired, + }).isRequired, blurTextBox: PropTypes.func.isRequired, browseFileTypes: PropTypes.string, validMimeTypes: PropTypes.array, @@ -366,6 +369,7 @@ export default class AttachmentButton extends PureComponent { maxFileCount, onShowFileMaxWarning, extraOptions, + actions, } = this.props; if (fileCount === maxFileCount) { @@ -439,21 +443,7 @@ export default class AttachmentButton extends PureComponent { }); } - this.props.navigator.showModal({ - screen: 'OptionsModal', - title: '', - animationType: 'none', - passProps: { - items, - }, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - }); + actions.showModalOverCurrentContext('OptionsModal', {items}); }; render() { diff --git a/app/components/attachment_button.test.js b/app/components/attachment_button/attachment_button.test.js similarity index 96% rename from app/components/attachment_button.test.js rename to app/components/attachment_button/attachment_button.test.js index ae789c5c6..b98df3716 100644 --- a/app/components/attachment_button.test.js +++ b/app/components/attachment_button/attachment_button.test.js @@ -13,6 +13,9 @@ jest.mock('react-intl'); describe('AttachmentButton', () => { const baseProps = { + actions: { + showModalOverCurrentContext: jest.fn(), + }, theme: Preferences.THEMES.default, navigator: {}, blurTextBox: jest.fn(), diff --git a/app/components/attachment_button/index.js b/app/components/attachment_button/index.js new file mode 100644 index 000000000..d48573136 --- /dev/null +++ b/app/components/attachment_button/index.js @@ -0,0 +1,19 @@ +// 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 {showModalOverCurrentContext} from 'app/actions/navigation'; + +import AttachmentButton from './attachment_button'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModalOverCurrentContext, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(AttachmentButton); diff --git a/app/components/root/index.js b/app/components/root/index.js index c2234531c..747bfdb9c 100644 --- a/app/components/root/index.js +++ b/app/components/root/index.js @@ -1,13 +1,15 @@ // 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 {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general'; - -import {getCurrentLocale} from 'app/selectors/i18n'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + +import {resetToTeams} from 'app/actions/navigation'; +import {getCurrentLocale} from 'app/selectors/i18n'; import {removeProtocol} from 'app/utils/url'; import Root from './root'; @@ -23,4 +25,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(Root); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + resetToTeams, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Root); diff --git a/app/components/root/root.js b/app/components/root/root.js index 5566e7335..051c4c715 100644 --- a/app/components/root/root.js +++ b/app/components/root/root.js @@ -14,6 +14,9 @@ import {getTranslations} from 'app/i18n'; export default class Root extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + resetToTeams: PropTypes.func.isRequired, + }).isRequired, children: PropTypes.node, navigator: PropTypes.object, excludeEvents: PropTypes.bool, @@ -83,28 +86,24 @@ export default class Root extends PureComponent { } navigateToTeamsPage = (screen) => { - const {currentUrl, navigator, theme} = this.props; + const {currentUrl, theme, actions} = this.props; const {intl} = this.refs.provider.getChildContext(); - let navigatorButtons; let passProps = {theme}; + const options = {topBar: {}}; if (Platform.OS === 'android') { - navigatorButtons = { - rightButtons: [{ - title: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}), - id: 'logout', - buttonColor: theme.sidebarHeaderTextColor, - showAsAction: 'always', - }], - }; + options.topBar.rightButtons = [{ + id: 'logout', + text: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}), + color: theme.sidebarHeaderTextColor, + showAsAction: 'always', + }]; } else { - navigatorButtons = { - leftButtons: [{ - title: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}), - id: 'logout', - buttonColor: theme.sidebarHeaderTextColor, - }], - }; + options.topBar.leftButtons = [{ + id: 'logout', + text: intl.formatMessage({id: 'sidebar_right_menu.logout', defaultMessage: 'Logout'}), + color: theme.sidebarHeaderTextColor, + }]; } if (screen === 'SelectTeam') { @@ -115,20 +114,9 @@ export default class Root extends PureComponent { }; } - navigator.resetTo({ - screen, - title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}), - animated: false, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - navigatorButtons, - passProps, - }); + const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}); + + actions.resetToTeams(screen, title, passProps, options); } handleNotificationTapped = async () => { diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js index b69b77ec9..f1bd342cd 100644 --- a/app/components/safe_area_view/safe_area_view.ios.js +++ b/app/components/safe_area_view/safe_area_view.ios.js @@ -5,7 +5,6 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Dimensions, Keyboard, NativeModules, View} from 'react-native'; import SafeArea from 'react-native-safe-area'; -import {Navigation} from 'react-native-navigation'; import {DeviceTypes} from 'app/constants'; import mattermostManaged from 'app/mattermost_managed'; @@ -53,8 +52,6 @@ export default class SafeAreaIos extends PureComponent { } componentDidMount() { - this.navigationEventListener = Navigation.events().bindComponent(this); - Dimensions.addEventListener('change', this.getSafeAreaInsets); this.keyboardDidShowListener = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow); this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide); @@ -69,14 +66,6 @@ export default class SafeAreaIos extends PureComponent { this.mounted = false; } - componentDidAppear() { - this.getSafeAreaInsets(); - } - - componentDidDisappear() { - this.getSafeAreaInsets(); - } - getStatusBarHeight = () => { try { StatusBarManager.getHeight( 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 5940ff56f..e69b5ce57 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 @@ -2,11 +2,10 @@ exports[`ChannelItem should match snapshot 1`] = ` - - + `; exports[`ChannelItem should match snapshot for current user i.e currentUser (you) 1`] = ` - - + `; exports[`ChannelItem should match snapshot for current user i.e currentUser (you) when isSearchResult 1`] = ` - - + `; exports[`ChannelItem should match snapshot for deactivated user and is currentChannel 1`] = ` - - + `; exports[`ChannelItem should match snapshot for deactivated user and is searchResult 1`] = ` - - + `; @@ -561,11 +556,10 @@ exports[`ChannelItem should match snapshot for showUnreadForMsgs 1`] = `null`; exports[`ChannelItem should match snapshot with draft 1`] = ` - - + `; exports[`ChannelItem should match snapshot with mentions and muted 1`] = ` - - + `; 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 4e5301985..a5d01ffce 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 @@ -2,17 +2,17 @@ // See LICENSE.txt for license information. import React, {PureComponent} from 'react'; -import {General} from 'mattermost-redux/constants'; - import PropTypes from 'prop-types'; import { Animated, - Platform, TouchableHighlight, Text, View, } from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; + +import {General} from 'mattermost-redux/constants'; import Badge from 'app/components/badge'; import ChannelIcon from 'app/components/channel_icon'; @@ -32,7 +32,6 @@ export default class ChannelItem extends PureComponent { isUnread: PropTypes.bool, hasDraft: PropTypes.bool, mentions: PropTypes.number.isRequired, - navigator: PropTypes.object, onSelectChannel: PropTypes.func.isRequired, shouldHideChannel: PropTypes.bool, showUnreadForMsgs: PropTypes.bool.isRequired, @@ -40,6 +39,7 @@ export default class ChannelItem extends PureComponent { unreadMsgs: PropTypes.number.isRequired, isSearchResult: PropTypes.bool, isBot: PropTypes.bool.isRequired, + previewChannel: PropTypes.func, }; static defaultProps = { @@ -58,28 +58,25 @@ export default class ChannelItem extends PureComponent { }); }); - onPreview = () => { - const {channelId, navigator} = this.props; - if (Platform.OS === 'ios' && navigator && this.previewRef) { + onPreview = ({reactTag}) => { + const {channelId, previewChannel} = this.props; + if (previewChannel) { const {intl} = this.context; - - navigator.push({ - screen: 'ChannelPeek', - previewCommit: false, - previewView: this.previewRef, - previewActions: [{ - id: 'action-mark-as-read', - title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'}), - }], - passProps: { - channelId, + const passProps = { + channelId, + }; + const options = { + preview: { + reactTag, + actions: [{ + id: 'action-mark-as-read', + title: intl.formatMessage({id: 'mobile.channel.markAsRead', defaultMessage: 'Mark As Read'}), + }], }, - }); - } - }; + }; - setPreviewRef = (ref) => { - this.previewRef = ref; + previewChannel(passProps, options); + } }; showChannelAsUnread = () => { @@ -190,11 +187,12 @@ export default class ChannelItem extends PureComponent { ); return ( - - + {extraBorder} @@ -210,7 +208,7 @@ export default class ChannelItem extends PureComponent { {badge} - + ); } diff --git a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js index 8774c4614..38334340d 100644 --- a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js +++ b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js @@ -3,7 +3,7 @@ import React from 'react'; import {shallow} from 'enzyme'; -import {TouchableHighlight} from 'react-native'; +import {Navigation} from 'react-native-navigation'; import Preferences from 'mattermost-redux/constants/preferences'; @@ -216,7 +216,7 @@ describe('ChannelItem', () => { {context: {intl: {formatMessage: jest.fn()}}}, ); - wrapper.find(TouchableHighlight).simulate('press'); + wrapper.find(Navigation.TouchablePreview).simulate('press'); jest.runAllTimers(); const expectedChannelParams = {id: baseProps.channelId, display_name: baseProps.displayName, fake: channel.fake, type: channel.type}; diff --git a/app/components/sidebars/main/channels_list/channels_list.js b/app/components/sidebars/main/channels_list/channels_list.js index 4d5ec4d15..1a26c82f0 100644 --- a/app/components/sidebars/main/channels_list/channels_list.js +++ b/app/components/sidebars/main/channels_list/channels_list.js @@ -22,7 +22,6 @@ let FilteredList = null; export default class ChannelsList extends PureComponent { static propTypes = { - navigator: PropTypes.object, onJoinChannel: PropTypes.func.isRequired, onSearchEnds: PropTypes.func.isRequired, onSearchStart: PropTypes.func.isRequired, @@ -30,6 +29,7 @@ export default class ChannelsList extends PureComponent { onShowTeams: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, drawerOpened: PropTypes.bool, + previewChannel: PropTypes.func, }; static contextTypes = { @@ -86,9 +86,9 @@ export default class ChannelsList extends PureComponent { render() { const {intl} = this.context; const { - navigator, onShowTeams, theme, + previewChannel, } = this.props; const {searching, term} = this.state; @@ -101,14 +101,15 @@ export default class ChannelsList extends PureComponent { onSelectChannel={this.onSelectChannel} styles={styles} term={term} + previewChannel={previewChannel} /> ); } else { list = ( ); } diff --git a/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js b/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js index 7546f3630..d37d56c42 100644 --- a/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js +++ b/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js @@ -54,6 +54,7 @@ class FilteredList extends Component { styles: PropTypes.object.isRequired, term: PropTypes.string, theme: PropTypes.object.isRequired, + previewChannel: PropTypes.func, }; static defaultProps = { @@ -126,6 +127,7 @@ class FilteredList extends Component { isUnread={channel.isUnread} mentions={0} onSelectChannel={this.onSelectChannel} + previewChannel={this.props.previewChannel} /> ); }; diff --git a/app/components/sidebars/main/channels_list/list/index.js b/app/components/sidebars/main/channels_list/list/index.js index adb6f31a7..eaca23964 100644 --- a/app/components/sidebars/main/channels_list/list/index.js +++ b/app/components/sidebars/main/channels_list/list/index.js @@ -1,6 +1,7 @@ // 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 {General} from 'mattermost-redux/constants'; @@ -17,6 +18,8 @@ import {memoizeResult} from 'mattermost-redux/utils/helpers'; import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; +import {showModal} from 'app/actions/navigation'; + import {DeviceTypes, ViewTypes} from 'app/constants'; import List from './list'; @@ -67,6 +70,14 @@ function mapStateToProps(state) { }; } +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModal, + }, dispatch), + }; +} + function areStatesEqual(next, prev) { const equalRoles = getCurrentUserRoles(prev) === getCurrentUserRoles(next); const equalChannels = next.entities.channels === prev.entities.channels; @@ -77,4 +88,4 @@ function areStatesEqual(next, prev) { return equalChannels && equalConfig && equalRoles && equalUsers && equalFav; } -export default connect(mapStateToProps, null, null, {pure: true, areStatesEqual})(List); +export default connect(mapStateToProps, mapDispatchToProps, null, {pure: true, areStatesEqual})(List); diff --git a/app/components/sidebars/main/channels_list/list/list.js b/app/components/sidebars/main/channels_list/list/list.js index 27ce989e1..9e0e3101f 100644 --- a/app/components/sidebars/main/channels_list/list/list.js +++ b/app/components/sidebars/main/channels_list/list/list.js @@ -34,14 +34,17 @@ let UnreadIndicator = null; export default class List extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + showModal: PropTypes.func.isRequired, + }).isRequired, canCreatePrivateChannels: PropTypes.bool.isRequired, favoriteChannelIds: PropTypes.array.isRequired, - navigator: PropTypes.object, onSelectChannel: PropTypes.func.isRequired, unreadChannelIds: PropTypes.array.isRequired, styles: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, orderedChannelIds: PropTypes.array.isRequired, + previewChannel: PropTypes.func, }; static contextTypes = { @@ -159,7 +162,10 @@ export default class List extends PureComponent { }; showCreateChannelOptions = () => { - const {canCreatePrivateChannels, navigator} = this.props; + const { + canCreatePrivateChannels, + actions, + } = this.props; const items = []; const moreChannels = { @@ -197,117 +203,87 @@ export default class List extends PureComponent { } items.push(newConversation); - navigator.showModal({ - screen: 'OptionsModal', - title: '', - animationType: 'none', - passProps: { - items, - onItemPress: () => navigator.dismissModal({ - animationType: 'none', - }), + const screen = 'OptionsModal'; + const title = ''; + const passProps = { + items, + }; + const options = { + modalPresentationStyle: 'overCurrentContext', + layout: { + backgroundColor: 'transparent', }, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', + topBar: { + visible: false, + height: 0, }, - }); + animations: { + showModal: { + enable: false, + }, + dismissModal: { + enable: false, + }, + }, + }; + + actions.showModal(screen, title, passProps, options); }; goToCreatePublicChannel = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; + const screen = 'CreateChannel'; + const title = intl.formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}); + const passProps = { + channelType: General.OPEN_CHANNEL, + closeButton: this.closeButton, + }; - navigator.showModal({ - screen: 'CreateChannel', - animationType: 'slide-up', - title: intl.formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}), - backButtonTitle: '', - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelType: General.OPEN_CHANNEL, - closeButton: this.closeButton, - }, - }); + actions.showModal(screen, title, passProps); }); goToCreatePrivateChannel = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; + const screen = 'CreateChannel'; + const title = intl.formatMessage({id: 'mobile.create_channel.private', defaultMessage: 'New Private Channel'}); + const passProps = { + channelType: General.PRIVATE_CHANNEL, + closeButton: this.closeButton, + }; - navigator.showModal({ - screen: 'CreateChannel', - animationType: 'slide-up', - title: intl.formatMessage({id: 'mobile.create_channel.private', defaultMessage: 'New Private Channel'}), - backButtonTitle: '', - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelType: General.PRIVATE_CHANNEL, - closeButton: this.closeButton, - }, - }); + actions.showModal(screen, title, passProps); }); goToDirectMessages = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; - - navigator.showModal({ - screen: 'MoreDirectMessages', - title: intl.formatMessage({id: 'mobile.more_dms.title', defaultMessage: 'New Conversation'}), - animationType: 'slide-up', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - navigatorButtons: { + const screen = 'MoreDirectMessages'; + const title = intl.formatMessage({id: 'mobile.more_dms.title', defaultMessage: 'New Conversation'}); + const passProps = {}; + const options = { + topBar: { leftButtons: [{ id: 'close-dms', icon: this.closeButton, }], }, - }); + }; + + actions.showModal(screen, title, passProps, options); }); goToMoreChannels = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; + const screen = 'MoreChannels'; + const title = intl.formatMessage({id: 'more_channels.title', defaultMessage: 'More Channels'}); + const passProps = { + closeButton: this.closeButton, + }; - navigator.showModal({ - screen: 'MoreChannels', - animationType: 'slide-up', - title: intl.formatMessage({id: 'more_channels.title', defaultMessage: 'More Channels'}), - backButtonTitle: '', - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - closeButton: this.closeButton, - }, - }); + actions.showModal(screen, title, passProps); }); keyExtractor = (item) => item.id || item; @@ -349,15 +325,15 @@ export default class List extends PureComponent { }; renderItem = ({item}) => { - const {favoriteChannelIds, unreadChannelIds} = this.props; + const {favoriteChannelIds, unreadChannelIds, previewChannel} = this.props; return ( ); }; diff --git a/app/components/sidebars/main/main_sidebar.js b/app/components/sidebars/main/main_sidebar.js index c4f89b70a..1b048cc00 100644 --- a/app/components/sidebars/main/main_sidebar.js +++ b/app/components/sidebars/main/main_sidebar.js @@ -44,9 +44,9 @@ export default class ChannelSidebar extends Component { currentUserId: PropTypes.string.isRequired, deviceWidth: PropTypes.number.isRequired, isLandscape: PropTypes.bool.isRequired, - navigator: PropTypes.object, teamsCount: PropTypes.number.isRequired, theme: PropTypes.object.isRequired, + previewChannel: PropTypes.func, }; static contextTypes = { @@ -297,9 +297,9 @@ export default class ChannelSidebar extends Component { renderNavigationView = (drawerWidth) => { const { - navigator, teamsCount, theme, + previewChannel, } = this.props; const { @@ -331,7 +331,6 @@ export default class ChannelSidebar extends Component { > ); @@ -345,7 +344,6 @@ export default class ChannelSidebar extends Component { > ); @@ -362,7 +361,6 @@ export default class ChannelSidebar extends Component { navBarBackgroundColor={theme.sidebarBg} backgroundColor={theme.sidebarHeaderBg} footerColor={theme.sidebarHeaderBg} - navigator={navigator} > { const {intl} = this.context; - const {currentUrl, navigator, theme} = this.props; - - navigator.showModal({ - screen: 'SelectTeam', - title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}), - animationType: 'slide-up', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - navigatorButtons: { + const {currentUrl, theme, actions} = this.props; + const screen = 'SelectTeam'; + const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}); + const passProps = { + currentUrl, + theme, + }; + const options = { + topBar: { leftButtons: [{ id: 'close-teams', icon: this.closeButton, }], }, - passProps: { - currentUrl, - theme, - }, - }); + }; + + actions.showModal(screen, title, passProps, options); }); keyExtractor = (item) => { diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js index d53d567de..05d67f9ca 100644 --- a/app/screens/channel/channel.ios.js +++ b/app/screens/channel/channel.ios.js @@ -25,6 +25,15 @@ const CHANNEL_POST_TEXTBOX_CURSOR_CHANGE = 'onChannelTextBoxCursorChange'; const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange'; export default class ChannelIOS extends ChannelBase { + previewChannel = (passProps, options) => { + const {actions, componentId} = this.props; + const screen = 'ChannelPeek'; + + actions.peek(componentId, screen, passProps, options); + }; + + optionalProps = {previewChannel: this.previewChannel}; + render() { const {height} = Dimensions.get('window'); const { @@ -82,6 +91,6 @@ export default class ChannelIOS extends ChannelBase { ); - return this.renderChannel(drawerContent); + return this.renderChannel(drawerContent, this.optionalProps); } } diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js index da40eacda..53e40b4ef 100644 --- a/app/screens/channel/channel_base.js +++ b/app/screens/channel/channel_base.js @@ -39,6 +39,7 @@ export default class ChannelBase extends PureComponent { selectDefaultTeam: PropTypes.func.isRequired, selectInitialChannel: PropTypes.func.isRequired, recordLoadTime: PropTypes.func.isRequired, + peek: PropTypes.func.isRequired, }).isRequired, currentChannelId: PropTypes.string, channelsRequestFailed: PropTypes.bool, @@ -259,7 +260,7 @@ export default class ChannelBase extends PureComponent { } }; - renderChannel(drawerContent) { + renderChannel(drawerContent, optionalProps = {}) { const { channelsRequestFailed, currentChannelId, @@ -298,6 +299,7 @@ export default class ChannelBase extends PureComponent { ref={this.channelSidebarRef} blurPostTextBox={this.blurPostTextBox} navigator={navigator} + {...optionalProps} > { - const {actions, navigator} = this.props; + const {actions} = this.props; Keyboard.dismiss(); await actions.clearSearch(); - await actions.showSearchModal(navigator); + await actions.showSearchModal(); }); render() { diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/index.js b/app/screens/channel/channel_nav_bar/channel_search_button/index.js index 12e7f3d09..8bbb62cfb 100644 --- a/app/screens/channel/channel_nav_bar/channel_search_button/index.js +++ b/app/screens/channel/channel_nav_bar/channel_search_button/index.js @@ -6,7 +6,7 @@ import {connect} from 'react-redux'; import {clearSearch} from 'mattermost-redux/actions/search'; -import {showSearchModal} from 'app/actions/views/search'; +import {showSearchModal} from 'app/actions/navigation'; import ChannelSearchButton from './channel_search_button'; diff --git a/app/screens/channel/index.js b/app/screens/channel/index.js index 40b00d318..1c9fc4c26 100644 --- a/app/screens/channel/index.js +++ b/app/screens/channel/index.js @@ -19,6 +19,7 @@ import { import {connection} from 'app/actions/device'; import {recordLoadTime} from 'app/actions/views/root'; import {selectDefaultTeam} from 'app/actions/views/select_team'; +import {peek} from 'app/actions/navigation'; import {isLandscape} from 'app/selectors/device'; import Channel from './channel'; @@ -48,6 +49,7 @@ function mapDispatchToProps(dispatch) { recordLoadTime, startPeriodicStatusUpdates, stopPeriodicStatusUpdates, + peek, }, dispatch), }; } diff --git a/app/screens/channel_peek/channel_peek.js b/app/screens/channel_peek/channel_peek.js index 081952558..162cdd85b 100644 --- a/app/screens/channel_peek/channel_peek.js +++ b/app/screens/channel_peek/channel_peek.js @@ -21,12 +21,11 @@ export default class ChannelPeek extends PureComponent { currentUserId: PropTypes.string, lastViewedAt: PropTypes.number, navigator: PropTypes.object, - postIds: PropTypes.array.isRequired, + postIds: PropTypes.array, theme: PropTypes.object.isRequired, }; static defaultProps = { - postIds: [], postVisibility: 15, }; @@ -65,7 +64,7 @@ export default class ChannelPeek extends PureComponent { } getVisiblePostIds = (props) => { - return props.postIds.slice(0, 15); + return props.postIds?.slice(0, 15) || []; }; render() { diff --git a/app/screens/index.js b/app/screens/index.js index bd19a2f6d..e5ab8f5af 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -15,6 +15,7 @@ const navigator = { push: () => {}, // eslint-disable-line no-empty-function setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function setStyle: () => {}, // eslint-disable-line no-empty-function + setButtons: () => {}, // eslint-disable-line no-empty-function }; export function registerScreens(store, Provider) { diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js index 0330b8c59..ac118b36f 100644 --- a/app/screens/more_channels/more_channels.js +++ b/app/screens/more_channels/more_channels.js @@ -54,6 +54,7 @@ export default class MoreChannels extends PureComponent { this.searchTimeoutId = 0; this.page = -1; this.next = true; + this.mounted = false; this.state = { channels: props.channels.slice(0, General.CHANNELS_CHUNK_SIZE), @@ -86,10 +87,14 @@ export default class MoreChannels extends PureComponent { componentDidMount() { this.navigationEventListener = Navigation.events().bindComponent(this); - + this.mounted = true; this.doGetChannels(); } + componentWillUnmount() { + this.mounted = false; + } + componentWillReceiveProps(nextProps) { const {term} = this.state; let channels; @@ -138,7 +143,7 @@ export default class MoreChannels extends PureComponent { const {actions, currentTeamId} = this.props; const {loading, term} = this.state; - if (this.next && !loading && !term) { + if (this.next && !loading && !term && this.mounted) { this.setState({loading: true}, () => { actions.getChannels( currentTeamId, @@ -172,12 +177,14 @@ export default class MoreChannels extends PureComponent { }; loadedChannels = ({data}) => { - if (data && !data.length) { - this.next = false; - } + if (this.mounted) { + if (data && !data.length) { + this.next = false; + } - this.page += 1; - this.setState({loading: false}); + this.page += 1; + this.setState({loading: false}); + } }; onSelectChannel = async (id) => { diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index 1af96f318..0d8723af3 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -61,6 +61,7 @@ export default class MoreDirectMessages extends PureComponent { this.searchTimeoutId = 0; this.next = true; this.page = -1; + this.mounted = false; this.state = { profiles: [], @@ -77,10 +78,15 @@ export default class MoreDirectMessages extends PureComponent { componentDidMount() { this.navigationEventListener = Navigation.events().bindComponent(this); + this.mounted = true; this.getProfiles(); } + componentWillUnmount() { + this.mounted = false; + } + componentDidUpdate(prevProps) { const {componentId, theme} = this.props; const {selectedCount, startingConversation} = this.state; @@ -111,7 +117,7 @@ export default class MoreDirectMessages extends PureComponent { getProfiles = debounce(() => { const {loading, term} = this.state; - if (this.next && !loading && !term) { + if (this.next && !loading && !term && this.mounted) { this.setState({loading: true}, () => { const {actions, currentTeamId, restrictDirectMessage} = this.props; @@ -182,13 +188,15 @@ export default class MoreDirectMessages extends PureComponent { }; loadedProfiles = ({data}) => { - const {profiles} = this.state; - if (data && !data.length) { - this.next = false; - } + if (this.mounted) { + const {profiles} = this.state; + if (data && !data.length) { + this.next = false; + } - this.page += 1; - this.setState({loading: false, profiles: [...profiles, ...data]}); + this.page += 1; + this.setState({loading: false, profiles: [...profiles, ...data]}); + } }; makeDirectChannel = async (id) => { diff --git a/app/screens/options_modal/options_modal.js b/app/screens/options_modal/options_modal.js index 55835939d..062e8dac9 100644 --- a/app/screens/options_modal/options_modal.js +++ b/app/screens/options_modal/options_modal.js @@ -9,6 +9,7 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -22,12 +23,11 @@ const DURATION = 200; export default class OptionsModal extends PureComponent { static propTypes = { + componentId: PropTypes.string.isRequired, items: PropTypes.array.isRequired, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, - navigator: PropTypes.object, onCancelPress: PropTypes.func, - onItemPress: PropTypes.func, title: PropTypes.oneOfType([ PropTypes.string, PropTypes.object, @@ -36,7 +36,6 @@ export default class OptionsModal extends PureComponent { static defaultProps = { onCancelPress: emptyFunction, - onItemPress: emptyFunction, }; constructor(props) { @@ -69,16 +68,17 @@ export default class OptionsModal extends PureComponent { toValue: this.props.deviceHeight, duration: DURATION, }).start(() => { - this.props.navigator.dismissModal({ - animationType: 'none', - }); + Navigation.dismissModal(this.props.componentId); }); }; + onItemPress = () => { + Navigation.dismissModal(this.props.componentId); + } + render() { const { items, - onItemPress, title, } = this.props; @@ -89,7 +89,7 @@ export default class OptionsModal extends PureComponent { diff --git a/app/screens/search/search.js b/app/screens/search/search.js index 23ef8a56b..4bd53c82c 100644 --- a/app/screens/search/search.js +++ b/app/screens/search/search.js @@ -57,6 +57,7 @@ export default class Search extends PureComponent { selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string.isRequired, currentTeamId: PropTypes.string.isRequired, initialValue: PropTypes.string, isLandscape: PropTypes.bool.isRequired, @@ -146,7 +147,7 @@ export default class Search extends PureComponent { if (this.state.preview) { this.refs.preview.handleClose(); } else { - this.props.navigator.dismissModal(); + Navigation.dismissModal(this.props.componentId); } } } @@ -176,9 +177,8 @@ export default class Search extends PureComponent { }; cancelSearch = preventDoubleTap(() => { - const {navigator} = this.props; this.handleTextChanged('', true); - navigator.dismissModal({animationType: 'slide-down'}); + Navigation.dismissModal(this.props.componentId); }); goToThread = (post) => { @@ -211,7 +211,7 @@ export default class Search extends PureComponent { handleHashtagPress = (hashtag) => { if (this.showingPermalink) { - this.props.navigator.dismissModal(); + Navigation.dismissModal(this.props.componentId); this.handleClosePermalink(); } diff --git a/app/screens/select_team/index.js b/app/screens/select_team/index.js index edc3c6f2a..3cfe25e34 100644 --- a/app/screens/select_team/index.js +++ b/app/screens/select_team/index.js @@ -4,12 +4,13 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {handleTeamChange} from 'app/actions/views/select_team'; - import {getTeams, joinTeam} from 'mattermost-redux/actions/teams'; import {logout} from 'mattermost-redux/actions/users'; import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams'; +import {resetToChannel} from 'app/actions/navigation'; +import {handleTeamChange} from 'app/actions/views/select_team'; + import SelectTeam from './select_team.js'; function mapStateToProps(state) { @@ -26,6 +27,7 @@ function mapDispatchToProps(dispatch) { handleTeamChange, joinTeam, logout, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/select_team/select_team.js b/app/screens/select_team/select_team.js index 54661ee00..5b66520fb 100644 --- a/app/screens/select_team/select_team.js +++ b/app/screens/select_team/select_team.js @@ -45,10 +45,10 @@ export default class SelectTeam extends PureComponent { handleTeamChange: PropTypes.func.isRequired, joinTeam: PropTypes.func.isRequired, logout: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, currentUrl: PropTypes.string.isRequired, - navigator: PropTypes.object, userWithoutTeams: PropTypes.bool, teams: PropTypes.array.isRequired, theme: PropTypes.object, @@ -124,27 +124,15 @@ export default class SelectTeam extends PureComponent { }; close = () => { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + Navigation.dismissModal(this.props.componentId); }; goToChannelView = () => { - const {navigator, theme} = this.props; + const passProps = { + disableTermsModal: true, + }; - navigator.resetTo({ - screen: 'Channel', - animated: false, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - disableTermsModal: true, - }, - }); + this.props.actions.resetToChannel(passProps); }; onSelectTeam = async (team) => { diff --git a/app/screens/select_team/select_team.test.js b/app/screens/select_team/select_team.test.js index 19e3d6440..9d6adc9df 100644 --- a/app/screens/select_team/select_team.test.js +++ b/app/screens/select_team/select_team.test.js @@ -29,6 +29,7 @@ describe('SelectTeam', () => { handleTeamChange: jest.fn(), joinTeam: jest.fn(), logout: jest.fn(), + resetToChannel: jest.fn(), }; const baseProps = { diff --git a/ios/Mattermost/AppDelegate.m b/ios/Mattermost/AppDelegate.m index f261b1423..33cce9067 100644 --- a/ios/Mattermost/AppDelegate.m +++ b/ios/Mattermost/AppDelegate.m @@ -56,8 +56,6 @@ NSString* const NotificationClearAction = @"clear"; NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - self.window.backgroundColor = [UIColor whiteColor]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil]; os_log(OS_LOG_DEFAULT, "Mattermost started!!"); diff --git a/package-lock.json b/package-lock.json index c2c6bbba2..f2058a1c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15534,9 +15534,9 @@ "from": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab" }, "react-native-navigation": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-2.20.2.tgz", - "integrity": "sha512-Ok1x2bBFLcIGzYF5k9SFikkUSDr4MWp1zLYy6KernL4g20DE9flgPphOrllbOEDSA87NwDUv+QsUPbpOPeW1iw==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-2.21.1.tgz", + "integrity": "sha512-CcICsn02NhfUZtTEg5QnXn7MiAlewc+uA3Q+UBO3SZw2rN94j8FBmEhvbvhFSWecBzqUTRvsXueMFVo4ruTAcQ==", "requires": { "hoist-non-react-statics": "3.x.x", "lodash": "4.17.x", diff --git a/package.json b/package.json index 36a79f431..45878152d 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "react-native-keychain": "3.1.3", "react-native-linear-gradient": "2.5.4", "react-native-local-auth": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab", - "react-native-navigation": "2.20.2", + "react-native-navigation": "2.21.1", "react-native-notifications": "github:mattermost/react-native-notifications#f6d32b55ecda9f6a6765437c21b075e13ac64141", "react-native-passcode-status": "1.1.1", "react-native-permissions": "1.1.1", From 150253d3922040a10e9df3cade960abef2e6e035 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Mon, 24 Jun 2019 12:52:08 -0700 Subject: [PATCH 09/19] [MM-16012] [MM-16084] Update screens related to settings sidebar + update SearchResultPost screen and its children (#2913) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Check-style fix * Address review comments --- app/actions/navigation.js | 57 +++++++- app/components/at_mention/at_mention.js | 32 ++--- app/components/at_mention/index.js | 13 +- .../attachment_button/attachment_button.js | 26 +--- .../attachment_button.test.js | 1 - .../autocomplete_selector.js | 20 +-- app/components/autocomplete_selector/index.js | 2 + .../combined_system_message.js | 4 - .../combined_system_message/last_users.js | 6 - .../file_attachment_list/file_attachment.js | 3 - .../file_attachment_document.js | 30 ++-- .../file_attachment_document/index.js | 19 +++ .../file_attachment_list.js | 12 +- .../file_attachment_list.test.js | 2 + app/components/file_attachment_list/index.js | 2 + app/components/formatted_markdown_text.js | 2 - app/components/markdown/hashtag/hashtag.js | 19 ++- .../markdown/hashtag/hashtag.test.js | 16 +-- app/components/markdown/hashtag/index.js | 8 +- app/components/markdown/markdown.js | 7 - .../markdown/markdown_code_block/index.js | 13 +- .../markdown_code_block.js | 28 ++-- .../markdown/markdown_image/index.js | 13 +- .../markdown/markdown_image/markdown_image.js | 8 +- .../markdown/markdown_table/index.js | 13 +- .../markdown/markdown_table/markdown_table.js | 36 ++--- .../markdown/markdown_table_image/index.js | 13 +- .../markdown_table_image.js | 34 ++--- .../action_menu/action_menu.js | 3 - .../message_attachments/attachment_actions.js | 3 - .../message_attachments/attachment_fields.js | 3 - .../attachment_image.js | 8 +- .../attachment_image/index.js | 19 +++ .../message_attachments/attachment_pretext.js | 3 - .../message_attachments/attachment_text.js | 3 - .../message_attachments/attachment_title.js | 3 - .../message_attachments/message_attachment.js | 8 -- app/components/post/index.js | 3 + app/components/post/post.js | 50 ++----- .../post_add_channel_member.js | 3 - .../post_attachment_opengraph/index.js | 3 + .../post_attachment_opengraph.js | 5 +- .../post_attachment_opengraph.test.js | 1 + app/components/post_body/index.js | 13 +- app/components/post_body/post_body.js | 78 ++++------ app/components/post_body/post_body.test.js | 4 +- .../post_body_additional_content/index.js | 2 + .../post_body_additional_content.js | 11 +- app/components/reactions/index.js | 3 + app/components/reactions/reactions.js | 51 +++---- .../safe_area_view/safe_area_view.ios.js | 1 - app/components/sidebars/settings/index.js | 9 ++ .../sidebars/settings/settings_sidebar.js | 66 +++------ app/mattermost.js | 9 ++ app/screens/channel/channel.ios.js | 4 +- app/screens/client_upgrade/client_upgrade.js | 10 +- app/screens/client_upgrade/index.js | 3 + .../__snapshots__/edit_profile.test.js.snap | 28 ---- app/screens/edit_profile/edit_profile.js | 23 ++- app/screens/edit_profile/edit_profile.test.js | 18 +-- app/screens/edit_profile/index.js | 4 + app/screens/flagged_posts/flagged_posts.js | 64 +++------ app/screens/flagged_posts/index.js | 10 +- .../interactive_dialog/interactive_dialog.js | 1 - app/screens/login/login.js | 9 +- app/screens/login/login.test.js | 3 - app/screens/login_options/index.js | 4 +- app/screens/login_options/login_options.js | 9 +- app/screens/mfa/index.js | 3 + app/screens/mfa/mfa.js | 5 +- app/screens/options_modal/index.js | 13 +- app/screens/options_modal/options_modal.js | 9 +- app/screens/permalink/permalink.js | 1 - app/screens/pinned_posts/pinned_posts.js | 1 - app/screens/reaction_list/reaction_list.js | 1 - app/screens/recent_mentions/index.js | 10 +- .../recent_mentions/recent_mentions.js | 64 +++------ app/screens/search/index.js | 6 +- app/screens/search/search.js | 7 +- .../search_result_post/search_result_post.js | 2 - app/screens/select_server/select_server.js | 5 +- app/screens/select_team/index.js | 3 +- app/screens/select_team/select_team.js | 5 +- app/screens/select_team/select_team.test.js | 1 + .../display_settings/display_settings.js | 13 +- app/screens/settings/general/index.js | 7 +- app/screens/settings/general/settings.js | 136 +++++------------- app/store/ephemeral_store.js | 22 +++ app/utils/images.js | 33 ++--- 89 files changed, 614 insertions(+), 727 deletions(-) rename app/components/file_attachment_list/{ => file_attachment_document}/file_attachment_document.js (94%) create mode 100644 app/components/file_attachment_list/file_attachment_document/index.js rename app/components/message_attachments/{ => attachment_image}/attachment_image.js (95%) create mode 100644 app/components/message_attachments/attachment_image/index.js create mode 100644 app/store/ephemeral_store.js diff --git a/app/actions/navigation.js b/app/actions/navigation.js index 88880e01b..56eb887af 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -8,6 +8,8 @@ import merge from 'deepmerge'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import EphemeralStore from 'app/store/ephemeral_store'; + export function resetToChannel(passProps = {}) { return (dispatch, getState) => { const theme = getTheme(getState()); @@ -128,9 +130,11 @@ export function resetToTeams(name, title, passProps = {}, options = {}) { }; } -export function goToScreen(componentId, name, title, passProps = {}, options = {}) { +export function goToScreen(name, title, passProps = {}, options = {}) { return (dispatch, getState) => { - const theme = getTheme(getState()); + const state = getState(); + const componentId = EphemeralStore.getTopComponentId(); + const theme = getTheme(state); const defaultOptions = { layout: { backgroundColor: theme.centerChannelBg, @@ -162,6 +166,22 @@ export function goToScreen(componentId, name, title, passProps = {}, options = { }; } +export function popTopScreen() { + return () => { + const componentId = EphemeralStore.getTopComponentId(); + + Navigation.pop(componentId); + }; +} + +export function popToRoot() { + return () => { + const componentId = EphemeralStore.getTopComponentId(); + + Navigation.popToRoot(componentId); + }; +} + export function showModal(name, title, passProps = {}, options = {}) { return (dispatch, getState) => { const theme = getTheme(getState()); @@ -186,6 +206,8 @@ export function showModal(name, title, passProps = {}, options = {}) { color: theme.sidebarHeaderTextColor, text: title, }, + leftButtonColor: theme.sidebarHeaderTextColor, + rightButtonColor: theme.sidebarHeaderTextColor, }, }; @@ -257,8 +279,23 @@ export function showSearchModal(initialValue = '') { }; } -export function peek(componentId, name, passProps = {}, options = {}) { +export function dismissModal(options = {}) { return () => { + const componentId = EphemeralStore.getTopComponentId(); + + Navigation.dismissModal(componentId, options); + }; +} + +export function dismissAllModals(options = {}) { + return () => { + Navigation.dismissAllModals(options); + }; +} + +export function peek(name, passProps = {}, options = {}) { + return () => { + const componentId = EphemeralStore.getTopComponentId(); const defaultOptions = { preview: { commit: false, @@ -273,4 +310,16 @@ export function peek(componentId, name, passProps = {}, options = {}) { }, }); }; -} \ No newline at end of file +} + +export function setButtons(buttons = {leftButtons: [], rightButtons: []}) { + return () => { + const componentId = EphemeralStore.getTopComponentId(); + + Navigation.mergeOptions(componentId, { + topBar: { + ...buttons, + }, + }); + }; +} diff --git a/app/components/at_mention/at_mention.js b/app/components/at_mention/at_mention.js index e096c5e1b..d34bae8ff 100644 --- a/app/components/at_mention/at_mention.js +++ b/app/components/at_mention/at_mention.js @@ -3,7 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {Clipboard, Platform, Text} from 'react-native'; +import {Clipboard, Text} from 'react-native'; import {intlShape} from 'react-intl'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; @@ -14,10 +14,12 @@ import BottomSheet from 'app/utils/bottom_sheet'; export default class AtMention extends React.PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, isSearchResult: PropTypes.bool, mentionName: PropTypes.string.isRequired, mentionStyle: CustomPropTypes.Style, - navigator: PropTypes.object.isRequired, onPostPress: PropTypes.func, textStyle: CustomPropTypes.Style, teammateNameDisplay: PropTypes.string, @@ -48,29 +50,15 @@ export default class AtMention extends React.PureComponent { } goToUserProfile = () => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; - const options = { - screen: 'UserProfile', - title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}), - animated: true, - backButtonTitle: '', - passProps: { - userId: this.state.user.id, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const screen = 'UserProfile'; + const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}); + const passProps = { + userId: this.state.user.id, }; - if (Platform.OS === 'ios') { - navigator.push(options); - } else { - navigator.showModal(options); - } + actions.goToScreen(screen, title, passProps); }; getUserDetailsFromMentionName(props) { diff --git a/app/components/at_mention/index.js b/app/components/at_mention/index.js index 07ac8c733..857345ee5 100644 --- a/app/components/at_mention/index.js +++ b/app/components/at_mention/index.js @@ -1,12 +1,15 @@ // 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 {getUsersByUsername} from 'mattermost-redux/selectors/entities/users'; import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import AtMention from './at_mention'; function mapStateToProps(state) { @@ -17,4 +20,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(AtMention); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(AtMention); diff --git a/app/components/attachment_button/attachment_button.js b/app/components/attachment_button/attachment_button.js index 5c746c5d8..07a3257c0 100644 --- a/app/components/attachment_button/attachment_button.js +++ b/app/components/attachment_button/attachment_button.js @@ -42,7 +42,6 @@ export default class AttachmentButton extends PureComponent { fileCount: PropTypes.number, maxFileCount: PropTypes.number.isRequired, maxFileSize: PropTypes.number.isRequired, - navigator: PropTypes.object.isRequired, onShowFileMaxWarning: PropTypes.func, onShowFileSizeWarning: PropTypes.func, onShowUnsupportedMimeTypeWarning: PropTypes.func, @@ -343,21 +342,6 @@ export default class AttachmentButton extends PureComponent { } }; - handleFileAttachmentOption = (action) => { - this.props.navigator.dismissModal({ - animationType: 'none', - }); - - // Have to wait to launch the library attachment action. - // If we call the action after dismissModal with no delay then the - // Wix navigator will dismiss the library attachment modal as well. - setTimeout(() => { - if (typeof action === 'function') { - action(); - } - }, 100); - }; - showFileAttachmentOptions = () => { const { canBrowseFiles, @@ -382,7 +366,7 @@ export default class AttachmentButton extends PureComponent { if (canTakePhoto) { items.push({ - action: () => this.handleFileAttachmentOption(this.attachPhotoFromCamera), + action: this.attachPhotoFromCamera, text: { id: t('mobile.file_upload.camera_photo'), defaultMessage: 'Take Photo', @@ -393,7 +377,7 @@ export default class AttachmentButton extends PureComponent { if (canTakeVideo) { items.push({ - action: () => this.handleFileAttachmentOption(this.attachVideoFromCamera), + action: this.attachVideoFromCamera, text: { id: t('mobile.file_upload.camera_video'), defaultMessage: 'Take Video', @@ -404,7 +388,7 @@ export default class AttachmentButton extends PureComponent { if (canBrowsePhotoLibrary) { items.push({ - action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary), + action: this.attachFileFromLibrary, text: { id: t('mobile.file_upload.library'), defaultMessage: 'Photo Library', @@ -415,7 +399,7 @@ export default class AttachmentButton extends PureComponent { if (canBrowseVideoLibrary && Platform.OS === 'android') { items.push({ - action: () => this.handleFileAttachmentOption(this.attachVideoFromLibraryAndroid), + action: this.attachVideoFromLibraryAndroid, text: { id: t('mobile.file_upload.video'), defaultMessage: 'Video Library', @@ -426,7 +410,7 @@ export default class AttachmentButton extends PureComponent { if (canBrowseFiles) { items.push({ - action: () => this.handleFileAttachmentOption(this.attachFileFromFiles), + action: this.attachFileFromFiles, text: { id: t('mobile.file_upload.browse'), defaultMessage: 'Browse Files', diff --git a/app/components/attachment_button/attachment_button.test.js b/app/components/attachment_button/attachment_button.test.js index b98df3716..e513d3634 100644 --- a/app/components/attachment_button/attachment_button.test.js +++ b/app/components/attachment_button/attachment_button.test.js @@ -17,7 +17,6 @@ describe('AttachmentButton', () => { showModalOverCurrentContext: jest.fn(), }, theme: Preferences.THEMES.default, - navigator: {}, blurTextBox: jest.fn(), maxFileSize: 10, uploadFiles: jest.fn(), diff --git a/app/components/autocomplete_selector/autocomplete_selector.js b/app/components/autocomplete_selector/autocomplete_selector.js index 49cbc44d6..2817da990 100644 --- a/app/components/autocomplete_selector/autocomplete_selector.js +++ b/app/components/autocomplete_selector/autocomplete_selector.js @@ -18,6 +18,7 @@ export default class AutocompleteSelector extends PureComponent { static propTypes = { actions: PropTypes.shape({ setAutocompleteSelector: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, label: PropTypes.string, placeholder: PropTypes.string.isRequired, @@ -28,7 +29,6 @@ export default class AutocompleteSelector extends PureComponent { showRequiredAsterisk: PropTypes.bool, teammateNameDisplay: PropTypes.string, theme: PropTypes.object.isRequired, - navigator: PropTypes.object, onSelected: PropTypes.func, helpText: PropTypes.node, errorText: PropTypes.node, @@ -96,22 +96,12 @@ export default class AutocompleteSelector extends PureComponent { goToSelectorScreen = preventDoubleTap(() => { const {formatMessage} = this.context.intl; - const {navigator, theme, actions, dataSource, options, placeholder} = this.props; + const {actions, dataSource, options, placeholder} = this.props; + const screen = 'SelectorScreen'; + const title = placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'}); actions.setAutocompleteSelector(dataSource, this.handleSelect, options); - - navigator.push({ - backButtonTitle: '', - screen: 'SelectorScreen', - title: placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); render() { diff --git a/app/components/autocomplete_selector/index.js b/app/components/autocomplete_selector/index.js index dc77afea9..afb4c445c 100644 --- a/app/components/autocomplete_selector/index.js +++ b/app/components/autocomplete_selector/index.js @@ -6,6 +6,7 @@ import {connect} from 'react-redux'; import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; import {setAutocompleteSelector} from 'app/actions/views/post'; import AutocompleteSelector from './autocomplete_selector'; @@ -21,6 +22,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ setAutocompleteSelector, + goToScreen, }, dispatch), }; } diff --git a/app/components/combined_system_message/combined_system_message.js b/app/components/combined_system_message/combined_system_message.js index edf43bf40..72d6e82b0 100644 --- a/app/components/combined_system_message/combined_system_message.js +++ b/app/components/combined_system_message/combined_system_message.js @@ -177,7 +177,6 @@ export default class CombinedSystemMessage extends React.PureComponent { currentUserId: PropTypes.string.isRequired, currentUsername: PropTypes.string.isRequired, messageData: PropTypes.array.isRequired, - navigator: PropTypes.object.isRequired, showJoinLeave: PropTypes.bool.isRequired, textStyles: PropTypes.object, theme: PropTypes.object.isRequired, @@ -266,7 +265,6 @@ export default class CombinedSystemMessage extends React.PureComponent { const { currentUserId, currentUsername, - navigator, textStyles, theme, } = this.props; @@ -285,7 +283,6 @@ export default class CombinedSystemMessage extends React.PureComponent { diff --git a/app/components/combined_system_message/last_users.js b/app/components/combined_system_message/last_users.js index 5e026ad03..f85600ff7 100644 --- a/app/components/combined_system_message/last_users.js +++ b/app/components/combined_system_message/last_users.js @@ -53,7 +53,6 @@ export default class LastUsers extends React.PureComponent { static propTypes = { actor: PropTypes.string, expandedLocale: PropTypes.object.isRequired, - navigator: PropTypes.object.isRequired, postType: PropTypes.string.isRequired, style: PropTypes.object.isRequired, textStyles: PropTypes.object, @@ -88,7 +87,6 @@ export default class LastUsers extends React.PureComponent { const { actor, expandedLocale, - navigator, style, textStyles, usernames, @@ -106,7 +104,6 @@ export default class LastUsers extends React.PureComponent { return ( @@ -116,7 +113,6 @@ export default class LastUsers extends React.PureComponent { renderCollapsedView = () => { const { actor, - navigator, postType, style, textStyles, @@ -134,7 +130,6 @@ export default class LastUsers extends React.PureComponent { defaultMessage={'{firstUser} and '} values={{firstUser}} baseTextStyle={style.baseText} - navigator={navigator} style={style.baseText} textStyles={textStyles} theme={theme} @@ -155,7 +150,6 @@ export default class LastUsers extends React.PureComponent { defaultMessage={typeMessage[postType].defaultMessage} values={{actor}} baseTextStyle={style.baseText} - navigator={navigator} style={style.baseText} textStyles={textStyles} theme={theme} diff --git a/app/components/file_attachment_list/file_attachment.js b/app/components/file_attachment_list/file_attachment.js index f190cf0ee..6efe9563c 100644 --- a/app/components/file_attachment_list/file_attachment.js +++ b/app/components/file_attachment_list/file_attachment.js @@ -29,7 +29,6 @@ export default class FileAttachment extends PureComponent { onLongPress: PropTypes.func, onPreviewPress: PropTypes.func, theme: PropTypes.object.isRequired, - navigator: PropTypes.object, }; static defaultProps = { @@ -93,7 +92,6 @@ export default class FileAttachment extends PureComponent { deviceWidth, file, theme, - navigator, onLongPress, } = this.props; const {data} = file; @@ -120,7 +118,6 @@ export default class FileAttachment extends PureComponent { ref={this.setDocumentRef} canDownloadFiles={canDownloadFiles} file={file} - navigator={navigator} onLongPress={onLongPress} theme={theme} /> diff --git a/app/components/file_attachment_list/file_attachment_document.js b/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js similarity index 94% rename from app/components/file_attachment_list/file_attachment_document.js rename to app/components/file_attachment_list/file_attachment_document/file_attachment_document.js index 473463227..d699b9a29 100644 --- a/app/components/file_attachment_list/file_attachment_document.js +++ b/app/components/file_attachment_list/file_attachment_document/file_attachment_document.js @@ -25,7 +25,7 @@ import {DeviceTypes} from 'app/constants/'; import mattermostBucket from 'app/mattermost_bucket'; import {changeOpacity} from 'app/utils/theme'; -import FileAttachmentIcon from './file_attachment_icon'; +import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon'; const {DOCUMENTS_PATH} = DeviceTypes; const DOWNLOADING_OFFSET = 28; @@ -38,13 +38,15 @@ const circularProgressWidth = 4; export default class FileAttachmentDocument extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, backgroundColor: PropTypes.string, canDownloadFiles: PropTypes.bool.isRequired, iconHeight: PropTypes.number, iconWidth: PropTypes.number, file: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, - navigator: PropTypes.object, onLongPress: PropTypes.func, wrapperHeight: PropTypes.number, wrapperWidth: PropTypes.number, @@ -192,7 +194,7 @@ export default class FileAttachmentDocument extends PureComponent { }; previewTextFile = (file, delay = 2000) => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {data} = file; const prefix = Platform.OS === 'android' ? 'file:/' : ''; const path = `${DOCUMENTS_PATH}/${data.id}-${file.caption}`; @@ -200,21 +202,13 @@ export default class FileAttachmentDocument extends PureComponent { setTimeout(async () => { try { const content = await readFile; - navigator.push({ - screen: 'TextPreview', - title: file.caption, - animated: true, - backButtonTitle: '', - passProps: { - content, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const screen = 'TextPreview'; + const title = file.caption; + const passProps = { + content, + }; + + actions.goToScreen(screen, title, passProps); this.setState({downloading: false, progress: 0}); } catch (error) { RNFetchBlob.fs.unlink(path); diff --git a/app/components/file_attachment_list/file_attachment_document/index.js b/app/components/file_attachment_list/file_attachment_document/index.js new file mode 100644 index 000000000..753d2d8ff --- /dev/null +++ b/app/components/file_attachment_list/file_attachment_document/index.js @@ -0,0 +1,19 @@ +// 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 {goToScreen} from 'app/actions/navigation'; + +import FileAttachmentDocument from './file_attachment_document'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(FileAttachmentDocument); diff --git a/app/components/file_attachment_list/file_attachment_list.js b/app/components/file_attachment_list/file_attachment_list.js index f53eef22c..80f7d463c 100644 --- a/app/components/file_attachment_list/file_attachment_list.js +++ b/app/components/file_attachment_list/file_attachment_list.js @@ -20,14 +20,16 @@ import FileAttachment from './file_attachment'; export default class FileAttachmentList extends Component { static propTypes = { - actions: PropTypes.object.isRequired, + actions: PropTypes.shape({ + loadFilesForPostIfNecessary: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, + }).isRequired, canDownloadFiles: PropTypes.bool.isRequired, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, fileIds: PropTypes.array.isRequired, files: PropTypes.array, isFailed: PropTypes.bool, - navigator: PropTypes.object, onLongPress: PropTypes.func, postId: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, @@ -122,11 +124,12 @@ export default class FileAttachmentList extends Component { }; handlePreviewPress = preventDoubleTap((idx) => { - previewImageAtIndex(this.props.navigator, this.items, idx, this.galleryFiles); + const {actions} = this.props; + previewImageAtIndex(this.items, idx, this.galleryFiles, actions.showModalOverCurrentContext); }); renderItems = () => { - const {canDownloadFiles, deviceWidth, fileIds, files, navigator} = this.props; + const {canDownloadFiles, deviceWidth, fileIds, files} = this.props; if (!files.length && fileIds.length > 0) { return fileIds.map((id, idx) => ( @@ -156,7 +159,6 @@ export default class FileAttachmentList extends Component { file={f} id={file.id} index={idx} - navigator={navigator} onCaptureRef={this.handleCaptureRef} onPreviewPress={this.handlePreviewPress} onLongPress={this.props.onLongPress} diff --git a/app/components/file_attachment_list/file_attachment_list.test.js b/app/components/file_attachment_list/file_attachment_list.test.js index 245e0bac1..e2035d542 100644 --- a/app/components/file_attachment_list/file_attachment_list.test.js +++ b/app/components/file_attachment_list/file_attachment_list.test.js @@ -16,6 +16,7 @@ describe('PostAttachmentOpenGraph', () => { const baseProps = { actions: { loadFilesForPostIfNecessary, + showModalOverCurrentContext: jest.fn(), }, canDownloadFiles: true, deviceHeight: 680, @@ -71,6 +72,7 @@ describe('PostAttachmentOpenGraph', () => { files: [], actions: { loadFilesForPostIfNecessary: loadFilesForPostIfNecessaryMock, + showModalOverCurrentContext: jest.fn(), }, }; diff --git a/app/components/file_attachment_list/index.js b/app/components/file_attachment_list/index.js index 2230cd5df..29960f63a 100644 --- a/app/components/file_attachment_list/index.js +++ b/app/components/file_attachment_list/index.js @@ -8,6 +8,7 @@ import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/gene import {makeGetFilesForPost} from 'mattermost-redux/selectors/entities/files'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; import {loadFilesForPostIfNecessary} from 'app/actions/views/channel'; import {getDimensions} from 'app/selectors/device'; @@ -29,6 +30,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ loadFilesForPostIfNecessary, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/formatted_markdown_text.js b/app/components/formatted_markdown_text.js index 965280d45..2f5b03fc2 100644 --- a/app/components/formatted_markdown_text.js +++ b/app/components/formatted_markdown_text.js @@ -35,7 +35,6 @@ class FormattedMarkdownText extends React.PureComponent { baseTextStyle: CustomPropTypes.Style, defaultMessage: PropTypes.string.isRequired, id: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, onPostPress: PropTypes.func, style: CustomPropTypes.Style, textStyles: PropTypes.object, @@ -115,7 +114,6 @@ class FormattedMarkdownText extends React.PureComponent { diff --git a/app/components/markdown/hashtag/hashtag.js b/app/components/markdown/hashtag/hashtag.js index 75fc5486f..540d49bff 100644 --- a/app/components/markdown/hashtag/hashtag.js +++ b/app/components/markdown/hashtag/hashtag.js @@ -12,24 +12,31 @@ export default class Hashtag extends React.PureComponent { hashtag: PropTypes.string.isRequired, linkStyle: CustomPropTypes.Style.isRequired, onHashtagPress: PropTypes.func, - navigator: PropTypes.object.isRequired, actions: PropTypes.shape({ + popToRoot: PropTypes.func.isRequired, showSearchModal: PropTypes.func.isRequired, + dismissAllModals: PropTypes.func.isRequired, }).isRequired, }; handlePress = () => { - if (this.props.onHashtagPress) { - this.props.onHashtagPress(this.props.hashtag); + const { + onHashtagPress, + hashtag, + actions, + } = this.props; + + if (onHashtagPress) { + onHashtagPress(hashtag); return; } // Close thread view, permalink view, etc - this.props.navigator.dismissAllModals(); - this.props.navigator.popToRoot(); + actions.dismissAllModals(); + actions.popToRoot(); - this.props.actions.showSearchModal(this.props.navigator, '#' + this.props.hashtag); + actions.showSearchModal('#' + this.props.hashtag); }; render() { diff --git a/app/components/markdown/hashtag/hashtag.test.js b/app/components/markdown/hashtag/hashtag.test.js index 05d87e3dd..d75fc2a74 100644 --- a/app/components/markdown/hashtag/hashtag.test.js +++ b/app/components/markdown/hashtag/hashtag.test.js @@ -11,12 +11,10 @@ describe('Hashtag', () => { const baseProps = { hashtag: 'test', linkStyle: {color: 'red'}, - navigator: { - dismissAllModals: jest.fn(), - popToRoot: jest.fn(), - }, actions: { showSearchModal: jest.fn(), + dismissAllModals: jest.fn(), + popToRoot: jest.fn(), }, }; @@ -35,9 +33,9 @@ describe('Hashtag', () => { wrapper.find(Text).simulate('press'); - expect(props.navigator.dismissAllModals).toHaveBeenCalled(); - expect(props.navigator.popToRoot).toHaveBeenCalled(); - expect(props.actions.showSearchModal).toHaveBeenCalledWith(props.navigator, '#test'); + expect(props.actions.dismissAllModals).toHaveBeenCalled(); + expect(props.actions.popToRoot).toHaveBeenCalled(); + expect(props.actions.showSearchModal).toHaveBeenCalledWith('#test'); }); test('should call onHashtagPress if provided', () => { @@ -50,8 +48,8 @@ describe('Hashtag', () => { wrapper.find(Text).simulate('press'); - expect(props.navigator.dismissAllModals).not.toBeCalled(); - expect(props.navigator.popToRoot).not.toBeCalled(); + expect(props.actions.dismissAllModals).not.toBeCalled(); + expect(props.actions.popToRoot).not.toBeCalled(); expect(props.actions.showSearchModal).not.toBeCalled(); expect(props.onHashtagPress).toBeCalled(); diff --git a/app/components/markdown/hashtag/index.js b/app/components/markdown/hashtag/index.js index 3683f71ab..9ffc10e5c 100644 --- a/app/components/markdown/hashtag/index.js +++ b/app/components/markdown/hashtag/index.js @@ -4,14 +4,20 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {showSearchModal} from 'app/actions/views/search'; +import { + popToRoot, + showSearchModal, + dismissAllModals, +} from 'app/actions/navigation'; import Hashtag from './hashtag'; function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ + popToRoot, showSearchModal, + dismissAllModals, }, dispatch), }; } diff --git a/app/components/markdown/markdown.js b/app/components/markdown/markdown.js index 80aa2df39..93edb1296 100644 --- a/app/components/markdown/markdown.js +++ b/app/components/markdown/markdown.js @@ -49,7 +49,6 @@ export default class Markdown extends PureComponent { isSearchResult: PropTypes.bool, mentionKeys: PropTypes.array.isRequired, minimumHashtagLength: PropTypes.number.isRequired, - navigator: PropTypes.object.isRequired, onChannelLinkPress: PropTypes.func, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, @@ -176,7 +175,6 @@ export default class Markdown extends PureComponent { {reactChildren} @@ -188,7 +186,6 @@ export default class Markdown extends PureComponent { linkDestination={linkDestination} imagesMetadata={this.props.imagesMetadata} isReplyPost={this.props.isReplyPost} - navigator={this.props.navigator} source={src} errorTextStyle={[this.computeTextStyle(this.props.baseTextStyle, context), this.props.textStyles.error]} > @@ -209,7 +206,6 @@ export default class Markdown extends PureComponent { isSearchResult={this.props.isSearchResult} mentionName={mentionName} onPostPress={this.props.onPostPress} - navigator={this.props.navigator} /> ); }; @@ -250,7 +246,6 @@ export default class Markdown extends PureComponent { hashtag={hashtag} linkStyle={this.props.textStyles.link} onHashtagPress={this.props.onHashtagPress} - navigator={this.props.navigator} /> ); }; @@ -295,7 +290,6 @@ export default class Markdown extends PureComponent { return ( { return ( {children} diff --git a/app/components/markdown/markdown_code_block/index.js b/app/components/markdown/markdown_code_block/index.js index 37ccabce4..76e8cbb51 100644 --- a/app/components/markdown/markdown_code_block/index.js +++ b/app/components/markdown/markdown_code_block/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import MarkdownCodeBlock from './markdown_code_block'; function mapStateToProps(state) { @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(MarkdownCodeBlock); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(MarkdownCodeBlock); diff --git a/app/components/markdown/markdown_code_block/markdown_code_block.js b/app/components/markdown/markdown_code_block/markdown_code_block.js index d6ff764ed..eaef682e2 100644 --- a/app/components/markdown/markdown_code_block/markdown_code_block.js +++ b/app/components/markdown/markdown_code_block/markdown_code_block.js @@ -24,7 +24,9 @@ const MAX_LINES = 4; export default class MarkdownCodeBlock extends React.PureComponent { static propTypes = { - navigator: PropTypes.object.isRequired, + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, theme: PropTypes.object.isRequired, language: PropTypes.string, content: PropTypes.string.isRequired, @@ -40,10 +42,14 @@ export default class MarkdownCodeBlock extends React.PureComponent { }; handlePress = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions, language, content} = this.props; const {intl} = this.context; + const screen = 'Code'; + const passProps = { + content, + }; - const languageDisplayName = getDisplayNameForLanguage(this.props.language); + const languageDisplayName = getDisplayNameForLanguage(language); let title; if (languageDisplayName) { title = intl.formatMessage( @@ -62,21 +68,7 @@ export default class MarkdownCodeBlock extends React.PureComponent { }); } - navigator.push({ - screen: 'Code', - title, - animated: true, - backButtonTitle: '', - passProps: { - content: this.props.content, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title, passProps); }); handleLongPress = async () => { diff --git a/app/components/markdown/markdown_image/index.js b/app/components/markdown/markdown_image/index.js index 19ef387fa..f15c54ccc 100644 --- a/app/components/markdown/markdown_image/index.js +++ b/app/components/markdown/markdown_image/index.js @@ -1,10 +1,13 @@ // 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 {getCurrentUrl} from 'mattermost-redux/selectors/entities/general'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; + import {getDimensions} from 'app/selectors/device'; import MarkdownImage from './markdown_image'; @@ -16,4 +19,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(MarkdownImage); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModalOverCurrentContext, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(MarkdownImage); diff --git a/app/components/markdown/markdown_image/markdown_image.js b/app/components/markdown/markdown_image/markdown_image.js index 6f9fc9bf5..526bb4a82 100644 --- a/app/components/markdown/markdown_image/markdown_image.js +++ b/app/components/markdown/markdown_image/markdown_image.js @@ -33,13 +33,15 @@ const VIEWPORT_IMAGE_REPLY_OFFSET = 13; export default class MarkdownImage extends React.Component { static propTypes = { + actions: PropTypes.shape({ + showModalOverCurrentContext: PropTypes.func.isRequired, + }).isRequired, children: PropTypes.node, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, imagesMetadata: PropTypes.object, linkDestination: PropTypes.string, isReplyPost: PropTypes.bool, - navigator: PropTypes.object.isRequired, serverURL: PropTypes.string.isRequired, source: PropTypes.string.isRequired, errorTextStyle: CustomPropTypes.Style, @@ -175,6 +177,7 @@ export default class MarkdownImage extends React.Component { originalWidth, uri, } = this.state; + const {actions} = this.props; const link = this.getSource(); let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?')); const extension = filename.split('.').pop(); @@ -195,7 +198,8 @@ export default class MarkdownImage extends React.Component { localPath: uri, }, }]; - previewImageAtIndex(this.props.navigator, [this.refs.item], 0, files); + + previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext); }; loadImageSize = (source) => { diff --git a/app/components/markdown/markdown_table/index.js b/app/components/markdown/markdown_table/index.js index e3e37c016..4100a3fc4 100644 --- a/app/components/markdown/markdown_table/index.js +++ b/app/components/markdown/markdown_table/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import MarkdownTable from './markdown_table'; function mapStateToProps(state) { @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(MarkdownTable); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTable); diff --git a/app/components/markdown/markdown_table/markdown_table.js b/app/components/markdown/markdown_table/markdown_table.js index 2d6cf6d73..deffa3174 100644 --- a/app/components/markdown/markdown_table/markdown_table.js +++ b/app/components/markdown/markdown_table/markdown_table.js @@ -19,8 +19,10 @@ const MAX_HEIGHT = 300; export default class MarkdownTable extends React.PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, children: PropTypes.node.isRequired, - navigator: PropTypes.object.isRequired, numColumns: PropTypes.number.isRequired, theme: PropTypes.object.isRequired, }; @@ -44,27 +46,19 @@ export default class MarkdownTable extends React.PureComponent { }; handlePress = preventDoubleTap(() => { - const {navigator, theme} = this.props; - - navigator.push({ - screen: 'Table', - title: this.context.intl.formatMessage({ - id: 'mobile.routes.table', - defaultMessage: 'Table', - }), - animated: true, - backButtonTitle: '', - passProps: { - renderRows: this.renderRows, - tableWidth: this.getTableWidth(), - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const {actions} = this.props; + const {intl} = this.context; + const screen = 'Table'; + const title = intl.formatMessage({ + id: 'mobile.routes.table', + defaultMessage: 'Table', }); + const passProps = { + renderRows: this.renderRows, + tableWidth: this.getTableWidth(), + }; + + actions.goToScreen(screen, title, passProps); }); handleContainerLayout = (e) => { diff --git a/app/components/markdown/markdown_table_image/index.js b/app/components/markdown/markdown_table_image/index.js index 96174a3a8..c1eea7804 100644 --- a/app/components/markdown/markdown_table_image/index.js +++ b/app/components/markdown/markdown_table_image/index.js @@ -1,11 +1,14 @@ // 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 {getCurrentUrl} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import MarkdownTableImage from './markdown_table_image'; function mapStateToProps(state) { @@ -15,4 +18,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(MarkdownTableImage); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(MarkdownTableImage); diff --git a/app/components/markdown/markdown_table_image/markdown_table_image.js b/app/components/markdown/markdown_table_image/markdown_table_image.js index c75942201..b2fdfd5f3 100644 --- a/app/components/markdown/markdown_table_image/markdown_table_image.js +++ b/app/components/markdown/markdown_table_image/markdown_table_image.js @@ -11,10 +11,12 @@ import {preventDoubleTap} from 'app/utils/tap'; export default class MarkdownTableImage extends React.PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, children: PropTypes.node.isRequired, source: PropTypes.string.isRequired, textStyle: CustomPropTypes.Style.isRequired, - navigator: PropTypes.object.isRequired, serverURL: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, }; @@ -24,26 +26,18 @@ export default class MarkdownTableImage extends React.PureComponent { }; handlePress = preventDoubleTap(() => { - const {navigator, theme} = this.props; - - navigator.push({ - screen: 'TableImage', - title: this.context.intl.formatMessage({ - id: 'mobile.routes.tableImage', - defaultMessage: 'Image', - }), - animated: true, - backButtonTitle: '', - passProps: { - imageSource: this.getImageSource(), - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const {actions} = this.props; + const {intl} = this.context; + const screen = 'TableImage'; + const title = intl.formatMessage({ + id: 'mobile.routes.tableImage', + defaultMessage: 'Image', }); + const passProps = { + imageSource: this.getImageSource(), + }; + + actions.goToScreen(screen, title, passProps); }); getImageSource = () => { diff --git a/app/components/message_attachments/action_menu/action_menu.js b/app/components/message_attachments/action_menu/action_menu.js index 96197ecf0..3ea8d46fd 100644 --- a/app/components/message_attachments/action_menu/action_menu.js +++ b/app/components/message_attachments/action_menu/action_menu.js @@ -18,7 +18,6 @@ export default class ActionMenu extends PureComponent { options: PropTypes.arrayOf(PropTypes.object), postId: PropTypes.string.isRequired, selected: PropTypes.object, - navigator: PropTypes.object, }; constructor(props) { @@ -63,7 +62,6 @@ export default class ActionMenu extends PureComponent { name, dataSource, options, - navigator, } = this.props; const {selected} = this.state; @@ -73,7 +71,6 @@ export default class ActionMenu extends PureComponent { dataSource={dataSource} options={options} selected={selected} - navigator={navigator} onSelected={this.handleSelect} /> ); diff --git a/app/components/message_attachments/attachment_actions.js b/app/components/message_attachments/attachment_actions.js index 8b46580c2..67e77886e 100644 --- a/app/components/message_attachments/attachment_actions.js +++ b/app/components/message_attachments/attachment_actions.js @@ -10,14 +10,12 @@ import ActionButton from './action_button'; export default class AttachmentActions extends PureComponent { static propTypes = { actions: PropTypes.array, - navigator: PropTypes.object.isRequired, postId: PropTypes.string.isRequired, }; render() { const { actions, - navigator, postId, } = this.props; @@ -43,7 +41,6 @@ export default class AttachmentActions extends PureComponent { defaultOption={action.default_option} options={action.options} postId={postId} - navigator={navigator} /> ); break; diff --git a/app/components/message_attachments/attachment_fields.js b/app/components/message_attachments/attachment_fields.js index 67d76dda4..ba96553d0 100644 --- a/app/components/message_attachments/attachment_fields.js +++ b/app/components/message_attachments/attachment_fields.js @@ -15,7 +15,6 @@ export default class AttachmentFields extends PureComponent { blockStyles: PropTypes.object.isRequired, fields: PropTypes.array, metadata: PropTypes.object, - navigator: PropTypes.object.isRequired, onPermalinkPress: PropTypes.func, textStyles: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, @@ -27,7 +26,6 @@ export default class AttachmentFields extends PureComponent { blockStyles, fields, metadata, - navigator, onPermalinkPress, textStyles, theme, @@ -88,7 +86,6 @@ export default class AttachmentFields extends PureComponent { blockStyles={blockStyles} imagesMetadata={metadata?.images} value={(field.value || '')} - navigator={navigator} onPermalinkPress={onPermalinkPress} /> diff --git a/app/components/message_attachments/attachment_image.js b/app/components/message_attachments/attachment_image/attachment_image.js similarity index 95% rename from app/components/message_attachments/attachment_image.js rename to app/components/message_attachments/attachment_image/attachment_image.js index 249c1e9ec..dfb04ac13 100644 --- a/app/components/message_attachments/attachment_image.js +++ b/app/components/message_attachments/attachment_image/attachment_image.js @@ -15,11 +15,13 @@ const VIEWPORT_IMAGE_CONTAINER_OFFSET = 10; export default class AttachmentImage extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + showModalOverCurrentContext: PropTypes.func.isRequired, + }).isRequired, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, imageMetadata: PropTypes.object, imageUrl: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, }; @@ -47,7 +49,7 @@ export default class AttachmentImage extends PureComponent { } handlePreviewImage = () => { - const {imageUrl, navigator} = this.props; + const {actions, imageUrl} = this.props; const { imageUri: uri, originalHeight, @@ -73,7 +75,7 @@ export default class AttachmentImage extends PureComponent { localPath: uri, }, }]; - previewImageAtIndex(navigator, [this.refs.item], 0, files); + previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext); }; setImageDimensions = (imageUri, dimensions, originalWidth, originalHeight) => { diff --git a/app/components/message_attachments/attachment_image/index.js b/app/components/message_attachments/attachment_image/index.js new file mode 100644 index 000000000..17bfedae3 --- /dev/null +++ b/app/components/message_attachments/attachment_image/index.js @@ -0,0 +1,19 @@ +// 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 {showModalOverCurrentContext} from 'app/actions/navigation'; + +import AttachmentImage from './attachment_image'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModalOverCurrentContext, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(AttachmentImage); diff --git a/app/components/message_attachments/attachment_pretext.js b/app/components/message_attachments/attachment_pretext.js index fcb1e900d..827af5ca7 100644 --- a/app/components/message_attachments/attachment_pretext.js +++ b/app/components/message_attachments/attachment_pretext.js @@ -13,7 +13,6 @@ export default class AttachmentPreText extends PureComponent { baseTextStyle: CustomPropTypes.Style.isRequired, blockStyles: PropTypes.object.isRequired, metadata: PropTypes.object, - navigator: PropTypes.object.isRequired, onPermalinkPress: PropTypes.func, textStyles: PropTypes.object.isRequired, value: PropTypes.string, @@ -24,7 +23,6 @@ export default class AttachmentPreText extends PureComponent { baseTextStyle, blockStyles, metadata, - navigator, onPermalinkPress, value, textStyles, @@ -42,7 +40,6 @@ export default class AttachmentPreText extends PureComponent { blockStyles={blockStyles} imagesMetadata={metadata?.images} value={value} - navigator={navigator} onPermalinkPress={onPermalinkPress} /> diff --git a/app/components/message_attachments/attachment_text.js b/app/components/message_attachments/attachment_text.js index 66ef572ca..b155d5178 100644 --- a/app/components/message_attachments/attachment_text.js +++ b/app/components/message_attachments/attachment_text.js @@ -18,7 +18,6 @@ export default class AttachmentText extends PureComponent { deviceHeight: PropTypes.number.isRequired, hasThumbnail: PropTypes.bool, metadata: PropTypes.object, - navigator: PropTypes.object.isRequired, onPermalinkPress: PropTypes.func, textStyles: PropTypes.object.isRequired, value: PropTypes.string, @@ -68,7 +67,6 @@ export default class AttachmentText extends PureComponent { blockStyles, hasThumbnail, metadata, - navigator, onPermalinkPress, value, textStyles, @@ -97,7 +95,6 @@ export default class AttachmentText extends PureComponent { blockStyles={blockStyles} imagesMetadata={metadata?.images} value={value} - navigator={navigator} onPermalinkPress={onPermalinkPress} /> diff --git a/app/components/message_attachments/attachment_title.js b/app/components/message_attachments/attachment_title.js index bfd673edd..12a525748 100644 --- a/app/components/message_attachments/attachment_title.js +++ b/app/components/message_attachments/attachment_title.js @@ -13,7 +13,6 @@ export default class AttachmentTitle extends PureComponent { link: PropTypes.string, theme: PropTypes.object.isRequired, value: PropTypes.string, - navigator: PropTypes.object.isRequired, }; openLink = () => { @@ -28,7 +27,6 @@ export default class AttachmentTitle extends PureComponent { link, value, theme, - navigator, } = this.props; if (!value) { @@ -57,7 +55,6 @@ export default class AttachmentTitle extends PureComponent { disableChannelLink={true} autolinkedUrlSchemes={[]} mentionKeys={[]} - navigator={navigator} theme={theme} value={value} baseTextStyle={style.title} diff --git a/app/components/message_attachments/message_attachment.js b/app/components/message_attachments/message_attachment.js index eafba47aa..5e544b6c8 100644 --- a/app/components/message_attachments/message_attachment.js +++ b/app/components/message_attachments/message_attachment.js @@ -31,7 +31,6 @@ export default class MessageAttachment extends PureComponent { deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, metadata: PropTypes.object, - navigator: PropTypes.object.isRequired, postId: PropTypes.string.isRequired, onPermalinkPress: PropTypes.func, theme: PropTypes.object, @@ -46,7 +45,6 @@ export default class MessageAttachment extends PureComponent { deviceHeight, deviceWidth, metadata, - navigator, onPermalinkPress, postId, textStyles, @@ -70,7 +68,6 @@ export default class MessageAttachment extends PureComponent { baseTextStyle={baseTextStyle} blockStyles={blockStyles} metadata={metadata} - navigator={navigator} onPermalinkPress={onPermalinkPress} textStyles={textStyles} value={attachment.pretext} @@ -86,7 +83,6 @@ export default class MessageAttachment extends PureComponent { link={attachment.title_link} theme={theme} value={attachment.title} - navigator={navigator} /> diff --git a/app/components/post/index.js b/app/components/post/index.js index bd501d2ff..300c7feaf 100644 --- a/app/components/post/index.js +++ b/app/components/post/index.js @@ -12,6 +12,7 @@ import {getUser, getCurrentUserId} from 'mattermost-redux/selectors/entities/use import {getMyPreferences, getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {isPostFlagged, isSystemMessage} from 'mattermost-redux/utils/post_utils'; +import {goToScreen, showModalOverCurrentContext} from 'app/actions/navigation'; import {insertToDraft, setPostTooltipVisible} from 'app/actions/views/channel'; import Post from './post'; @@ -93,6 +94,8 @@ function mapDispatchToProps(dispatch) { removePost, setPostTooltipVisible, insertToDraft, + goToScreen, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/post/post.js b/app/components/post/post.js index 0fc4e6f85..4345e0955 100644 --- a/app/components/post/post.js +++ b/app/components/post/post.js @@ -34,6 +34,8 @@ export default class Post extends PureComponent { createPost: PropTypes.func.isRequired, insertToDraft: PropTypes.func.isRequired, removePost: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, channelIsReadOnly: PropTypes.bool, currentUserId: PropTypes.string.isRequired, @@ -49,7 +51,6 @@ export default class Post extends PureComponent { isSearchResult: PropTypes.bool, commentedOnPost: PropTypes.object, managedConfig: PropTypes.object.isRequired, - navigator: PropTypes.object, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, shouldRenderReplyButton: PropTypes.bool, @@ -88,30 +89,16 @@ export default class Post extends PureComponent { goToUserProfile = () => { const {intl} = this.context; - const {navigator, post, theme} = this.props; - const options = { - screen: 'UserProfile', - title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}), - animated: true, - backButtonTitle: '', - passProps: { - userId: post.user_id, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const {actions, post} = this.props; + const screen = 'UserProfile'; + const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}); + const passProps = { + userId: post.user_id, }; Keyboard.dismiss(); requestAnimationFrame(() => { - if (Platform.OS === 'ios') { - navigator.push(options); - } else { - navigator.showModal(options); - } + actions.goToScreen(screen, title, passProps); }); }; @@ -120,7 +107,8 @@ export default class Post extends PureComponent { }; handleFailedPostPress = () => { - const options = { + const screen = 'OptionsModal'; + const passProps = { title: { id: t('mobile.post.failed_title'), defaultMessage: 'Unable to send your message:', @@ -151,22 +139,7 @@ export default class Post extends PureComponent { }], }; - this.props.navigator.showModal({ - screen: 'OptionsModal', - title: '', - animationType: 'none', - passProps: { - items: options.items, - title: options.title, - }, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - }); + this.props.actions.showModalOverCurrentContext(screen, passProps); }; handlePress = preventDoubleTap(() => { @@ -352,7 +325,6 @@ export default class Post extends PureComponent { channelIsReadOnly={channelIsReadOnly} isLastPost={isLastPost} isSearchResult={isSearchResult} - navigator={this.props.navigator} onFailedPostPress={this.handleFailedPostPress} onHashtagPress={onHashtagPress} onPermalinkPress={onPermalinkPress} diff --git a/app/components/post_add_channel_member/post_add_channel_member.js b/app/components/post_add_channel_member/post_add_channel_member.js index 84fa69f37..de79f312f 100644 --- a/app/components/post_add_channel_member/post_add_channel_member.js +++ b/app/components/post_add_channel_member/post_add_channel_member.js @@ -31,7 +31,6 @@ export default class PostAddChannelMember extends React.PureComponent { userIds: PropTypes.array.isRequired, usernames: PropTypes.array.isRequired, noGroupsUsernames: PropTypes.array, - navigator: PropTypes.object.isRequired, onPostPress: PropTypes.func, textStyles: PropTypes.object, }; @@ -90,7 +89,6 @@ export default class PostAddChannelMember extends React.PureComponent { mentionStyle={this.props.textStyles.mention} mentionName={usernames[0]} onPostPress={this.props.onPostPress} - navigator={this.props.navigator} /> ); } else if (usernames.length > 1) { @@ -119,7 +117,6 @@ export default class PostAddChannelMember extends React.PureComponent { mentionStyle={this.props.textStyles.mention} mentionName={username} onPostPress={this.props.onPostPress} - navigator={this.props.navigator} /> ); }).reduce((acc, el, idx, arr) => { diff --git a/app/components/post_attachment_opengraph/index.js b/app/components/post_attachment_opengraph/index.js index f422ddf5e..d18e1585b 100644 --- a/app/components/post_attachment_opengraph/index.js +++ b/app/components/post_attachment_opengraph/index.js @@ -6,6 +6,8 @@ import {bindActionCreators} from 'redux'; import {getOpenGraphMetadata} from 'mattermost-redux/actions/posts'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; + import {getDimensions} from 'app/selectors/device'; import PostAttachmentOpenGraph from './post_attachment_opengraph'; @@ -20,6 +22,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ getOpenGraphMetadata, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.js index d74293ea7..9ae9ace99 100644 --- a/app/components/post_attachment_opengraph/post_attachment_opengraph.js +++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.js @@ -28,13 +28,13 @@ export default class PostAttachmentOpenGraph extends PureComponent { static propTypes = { actions: PropTypes.shape({ getOpenGraphMetadata: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, imagesMetadata: PropTypes.object, isReplyPost: PropTypes.bool, link: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, openGraphData: PropTypes.object, theme: PropTypes.object.isRequired, }; @@ -181,6 +181,7 @@ export default class PostAttachmentOpenGraph extends PureComponent { originalWidth, originalHeight, } = this.state; + const {actions} = this.props; const filename = this.getFilename(link); const files = [{ @@ -195,7 +196,7 @@ export default class PostAttachmentOpenGraph extends PureComponent { }, }]; - previewImageAtIndex(this.props.navigator, [this.refs.item], 0, files); + previewImageAtIndex([this.refs.item], 0, files, actions.showModalOverCurrentContext); }; renderDescription = () => { diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js index d86e41e65..4d03ae92d 100644 --- a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js +++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js @@ -25,6 +25,7 @@ describe('PostAttachmentOpenGraph', () => { const baseProps = { actions: { getOpenGraphMetadata: jest.fn(), + showModalOverCurrentContext: jest.fn(), }, deviceHeight: 600, deviceWidth: 400, diff --git a/app/components/post_body/index.js b/app/components/post_body/index.js index 7830d1eaa..01c0de74a 100644 --- a/app/components/post_body/index.js +++ b/app/components/post_body/index.js @@ -1,6 +1,7 @@ // 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 {General, Posts} from 'mattermost-redux/constants'; @@ -20,6 +21,8 @@ import { } from 'mattermost-redux/utils/post_utils'; import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from 'mattermost-redux/utils/user_utils'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; + import {getDimensions} from 'app/selectors/device'; import {hasEmojisOnly} from 'app/utils/emoji_utils'; @@ -99,4 +102,12 @@ function makeMapStateToProps() { }; } -export default connect(makeMapStateToProps, null, null, {forwardRef: true})(PostBody); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModalOverCurrentContext, + }, dispatch), + }; +} + +export default connect(makeMapStateToProps, mapDispatchToProps, null, {forwardRef: true})(PostBody); diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js index 018905393..26e279fe3 100644 --- a/app/components/post_body/post_body.js +++ b/app/components/post_body/post_body.js @@ -36,6 +36,9 @@ const SHOW_MORE_HEIGHT = 60; export default class PostBody extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + showModalOverCurrentContext: PropTypes.func.isRequired, + }).isRequired, canDelete: PropTypes.bool, channelIsReadOnly: PropTypes.bool.isRequired, deviceHeight: PropTypes.number.isRequired, @@ -56,7 +59,6 @@ export default class PostBody extends PureComponent { metadata: PropTypes.object, managedConfig: PropTypes.object, message: PropTypes.string, - navigator: PropTypes.object.isRequired, onFailedPostPress: PropTypes.func, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, @@ -130,31 +132,25 @@ export default class PostBody extends PureComponent { openLongPost = preventDoubleTap(() => { const { managedConfig, - navigator, onHashtagPress, onPermalinkPress, post, + actions, } = this.props; - + const screen = 'LongPost'; + const passProps = { + postId: post.id, + managedConfig, + onHashtagPress, + onPermalinkPress, + }; const options = { - screen: 'LongPost', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - postId: post.id, - managedConfig, - onHashtagPress, - onPermalinkPress, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); }); showPostOptions = () => { @@ -168,10 +164,10 @@ export default class PostBody extends PureComponent { isPostEphemeral, isSystemMessage, managedConfig, - navigator, post, showAddReaction, location, + actions, } = this.props; if (isSystemMessage && (!canDelete || hasBeenDeleted)) { @@ -182,32 +178,22 @@ export default class PostBody extends PureComponent { return; } - const options = { - screen: 'PostOptions', - animationType: 'none', - backButtonTitle: '', - navigatorStyle: { - navBarHidden: true, - navBarTransparent: true, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - canDelete, - channelIsReadOnly, - hasBeenDeleted, - isFlagged, - isSystemMessage, - post, - managedConfig, - showAddReaction, - location, - }, + const screen = 'PostOptions'; + const passProps = { + canDelete, + channelIsReadOnly, + hasBeenDeleted, + isFlagged, + isSystemMessage, + post, + managedConfig, + showAddReaction, + location, }; Keyboard.dismiss(); requestAnimationFrame(() => { - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps); }); }; @@ -231,7 +217,6 @@ export default class PostBody extends PureComponent { return ( ); } @@ -281,7 +264,6 @@ export default class PostBody extends PureComponent { isSystemMessage, message, metadata, - navigator, onHashtagPress, onPermalinkPress, post, @@ -304,7 +286,6 @@ export default class PostBody extends PureComponent { ); }; @@ -356,7 +335,6 @@ export default class PostBody extends PureComponent { isSystemMessage, message, metadata, - navigator, onFailedPostPress, onHashtagPress, onPermalinkPress, @@ -395,7 +373,6 @@ export default class PostBody extends PureComponent { allUsernames={allUsernames} linkStyle={textStyles.link} messageData={messageData} - navigator={navigator} textStyles={textStyles} theme={theme} /> @@ -424,7 +401,6 @@ export default class PostBody extends PureComponent { isEdited={hasBeenEdited} isReplyPost={isReplyPost} isSearchResult={isSearchResult} - navigator={navigator} onHashtagPress={onHashtagPress} onPermalinkPress={onPermalinkPress} onPostPress={onPress} diff --git a/app/components/post_body/post_body.test.js b/app/components/post_body/post_body.test.js index aa78520cb..94dcbc48c 100644 --- a/app/components/post_body/post_body.test.js +++ b/app/components/post_body/post_body.test.js @@ -12,6 +12,9 @@ import PostBody from './post_body.js'; describe('PostBody', () => { const baseProps = { + actions: { + showModalOverCurrentContext: jest.fn(), + }, canDelete: true, channelIsReadOnly: false, deviceHeight: 1920, @@ -30,7 +33,6 @@ describe('PostBody', () => { isSystemMessage: false, managedConfig: {}, message: 'Hello, World!', - navigator: {}, onFailedPostPress: jest.fn(), onHashtagPress: jest.fn(), onPermalinkPress: jest.fn(), diff --git a/app/components/post_body_additional_content/index.js b/app/components/post_body_additional_content/index.js index 481b1b5ec..b55ed5ffa 100644 --- a/app/components/post_body_additional_content/index.js +++ b/app/components/post_body_additional_content/index.js @@ -10,6 +10,7 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/posts'; import {getBool, getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; import {ViewTypes} from 'app/constants'; import {getDimensions} from 'app/selectors/device'; import {extractFirstLink} from 'app/utils/url'; @@ -73,6 +74,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ getRedirectLocation, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/post_body_additional_content/post_body_additional_content.js b/app/components/post_body_additional_content/post_body_additional_content.js index 4adcdc806..80c925615 100644 --- a/app/components/post_body_additional_content/post_body_additional_content.js +++ b/app/components/post_body_additional_content/post_body_additional_content.js @@ -35,6 +35,7 @@ export default class PostBodyAdditionalContent extends PureComponent { static propTypes = { actions: PropTypes.shape({ getRedirectLocation: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, baseTextStyle: CustomPropTypes.Style, blockStyles: PropTypes.object, @@ -45,7 +46,6 @@ export default class PostBodyAdditionalContent extends PureComponent { isReplyPost: PropTypes.bool, link: PropTypes.string, message: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, openGraphData: PropTypes.object, @@ -183,7 +183,7 @@ export default class PostBodyAdditionalContent extends PureComponent { return null; } - const {isReplyPost, link, metadata, navigator, openGraphData, showLinkPreviews, theme} = this.props; + const {isReplyPost, link, metadata, openGraphData, showLinkPreviews, theme} = this.props; const attachments = this.getMessageAttachment(); if (attachments) { return attachments; @@ -202,7 +202,6 @@ export default class PostBodyAdditionalContent extends PureComponent { { const {shortenedLink} = this.state; let {link} = this.props; - const {navigator} = this.props; + const {actions} = this.props; if (shortenedLink) { link = shortenedLink; } @@ -431,7 +428,7 @@ export default class PostBodyAdditionalContent extends PureComponent { }, }]; - previewImageAtIndex(navigator, [imageRef], 0, files); + previewImageAtIndex([imageRef], 0, files, actions.showModalOverCurrentContext); }; playYouTubeVideo = () => { diff --git a/app/components/reactions/index.js b/app/components/reactions/index.js index e778343b6..87c20b010 100644 --- a/app/components/reactions/index.js +++ b/app/components/reactions/index.js @@ -13,6 +13,7 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getChannel} from 'mattermost-redux/selectors/entities/channels'; +import {showModal, showModalOverCurrentContext} from 'app/actions/navigation'; import {addReaction} from 'app/actions/views/emoji'; import Reactions from './reactions'; @@ -63,6 +64,8 @@ function mapDispatchToProps(dispatch) { addReaction, getReactionsForPost, removeReaction, + showModal, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/reactions/reactions.js b/app/components/reactions/reactions.js index 3fead81a7..5548ed180 100644 --- a/app/components/reactions/reactions.js +++ b/app/components/reactions/reactions.js @@ -23,9 +23,10 @@ export default class Reactions extends PureComponent { addReaction: PropTypes.func.isRequired, getReactionsForPost: PropTypes.func.isRequired, removeReaction: PropTypes.func.isRequired, + showModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, currentUserId: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, position: PropTypes.oneOf(['right', 'left']), postId: PropTypes.string.isRequired, reactions: PropTypes.object, @@ -50,25 +51,18 @@ export default class Reactions extends PureComponent { } handleAddReaction = preventDoubleTap(() => { + const {actions, theme} = this.props; const {formatMessage} = this.context.intl; - const {navigator, theme} = this.props; + const screen = 'AddReaction'; + const title = formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}); MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => { - navigator.showModal({ - screen: 'AddReaction', - title: formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - closeButton: source, - onEmojiPress: this.handleAddReactionToPost, - }, - }); + const passProps = { + closeButton: source, + onEmojiPress: this.handleAddReactionToPost, + }; + + actions.showModal(screen, title, passProps); }); }); @@ -87,28 +81,18 @@ export default class Reactions extends PureComponent { }; showReactionList = () => { - const {navigator, postId} = this.props; + const {actions, postId} = this.props; - const options = { - screen: 'ReactionList', - animationType: 'none', - backButtonTitle: '', - navigatorStyle: { - navBarHidden: true, - navBarTransparent: true, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - postId, - }, + const screen = 'ReactionList'; + const passProps = { + postId, }; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps); } renderReactions = () => { - const {currentUserId, navigator, reactions, theme, postId} = this.props; + const {currentUserId, reactions, theme, postId} = this.props; const highlightedReactions = []; const reactionsByName = Object.values(reactions).reduce((acc, reaction) => { if (acc.has(reaction.emoji_name)) { @@ -131,7 +115,6 @@ export default class Reactions extends PureComponent { count={reactionsByName.get(r).length} emojiName={r} highlight={highlightedReactions.includes(r)} - navigator={navigator} onPress={this.handleReactionPress} onLongPress={this.showReactionList} postId={postId} diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js index f1bd342cd..4758c992e 100644 --- a/app/components/safe_area_view/safe_area_view.ios.js +++ b/app/components/safe_area_view/safe_area_view.ios.js @@ -21,7 +21,6 @@ export default class SafeAreaIos extends PureComponent { forceTop: PropTypes.number, keyboardOffset: PropTypes.number.isRequired, navBarBackgroundColor: PropTypes.string, - navigator: PropTypes.object, headerComponent: PropTypes.node, theme: PropTypes.object.isRequired, }; diff --git a/app/components/sidebars/settings/index.js b/app/components/sidebars/settings/index.js index 140b697d0..66406da3f 100644 --- a/app/components/sidebars/settings/index.js +++ b/app/components/sidebars/settings/index.js @@ -8,6 +8,12 @@ import {logout, setStatus} from 'mattermost-redux/actions/users'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentUser, getStatusForUserId} from 'mattermost-redux/selectors/entities/users'; +import { + showModal, + showModalOverCurrentContext, + dismissModal, +} from 'app/actions/navigation'; + import {isLandscape, getDimensions} from 'app/selectors/device'; import SettingsSidebar from './settings_sidebar'; @@ -30,6 +36,9 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ logout, setStatus, + showModal, + showModalOverCurrentContext, + dismissModal, }, dispatch), }; } diff --git a/app/components/sidebars/settings/settings_sidebar.js b/app/components/sidebars/settings/settings_sidebar.js index 726b9db4f..9a3fff5b3 100644 --- a/app/components/sidebars/settings/settings_sidebar.js +++ b/app/components/sidebars/settings/settings_sidebar.js @@ -37,13 +37,15 @@ export default class SettingsDrawer extends PureComponent { actions: PropTypes.shape({ logout: PropTypes.func.isRequired, setStatus: PropTypes.func.isRequired, + showModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, blurPostTextBox: PropTypes.func.isRequired, children: PropTypes.node, currentUser: PropTypes.object.isRequired, deviceWidth: PropTypes.number.isRequired, isLandscape: PropTypes.bool.isRequired, - navigator: PropTypes.object, status: PropTypes.string, theme: PropTypes.object.isRequired, }; @@ -107,6 +109,7 @@ export default class SettingsDrawer extends PureComponent { }; handleSetStatus = preventDoubleTap(() => { + const {actions} = this.props; const items = [{ action: () => this.setStatus(General.ONLINE), text: { @@ -133,21 +136,7 @@ export default class SettingsDrawer extends PureComponent { }, }]; - this.props.navigator.showModal({ - screen: 'OptionsModal', - title: '', - animationType: 'none', - passProps: { - items, - }, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - }); + actions.showModalOverCurrentContext('OptionsModal', {items}); }); goToEditProfile = preventDoubleTap(() => { @@ -194,9 +183,6 @@ export default class SettingsDrawer extends PureComponent { goToSettings = preventDoubleTap(() => { const {intl} = this.context; - // TODO: Ensure all styles used in app/utils/theme's setNavigatorStyles - // are passed to Settings when this showModal call is updated to RNN v2, - // then remove setNavigatorStyles call in app/screens/settings/general/settings.js this.openModal( 'Settings', intl.formatMessage({id: 'mobile.routes.settings', defaultMessage: 'Settings'}), @@ -210,31 +196,20 @@ export default class SettingsDrawer extends PureComponent { }); openModal = (screen, title, passProps) => { - const {navigator, theme} = this.props; - this.closeSettingsSidebar(); + const {actions} = this.props; + const options = { + topBar: { + leftButtons: [{ + id: 'close-settings', + icon: this.closeButton, + }], + }, + }; + InteractionManager.runAfterInteractions(() => { - navigator.showModal({ - screen, - title, - animationType: 'slide-up', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - navigatorButtons: { - leftButtons: [{ - id: 'close-settings', - icon: this.closeButton, - }], - }, - passProps, - }); + actions.showModal(screen, title, passProps, options); }); }; @@ -254,7 +229,7 @@ export default class SettingsDrawer extends PureComponent { }; renderNavigationView = () => { - const {currentUser, navigator, theme} = this.props; + const {currentUser, theme} = this.props; const style = getStyleSheet(theme); return ( @@ -264,7 +239,6 @@ export default class SettingsDrawer extends PureComponent { footerColor={theme.centerChannelBg} footerComponent={} headerComponent={} - navigator={navigator} theme={theme} > @@ -359,12 +333,10 @@ export default class SettingsDrawer extends PureComponent { }; setStatus = (status) => { - const {status: currentUserStatus, navigator} = this.props; + const {status: currentUserStatus, actions} = this.props; if (currentUserStatus === General.OUT_OF_OFFICE) { - navigator.dismissModal({ - animationType: 'none', - }); + actions.dismissModal(); this.closeSettingsSidebar(); this.confirmReset(status); return; diff --git a/app/mattermost.js b/app/mattermost.js index 106a3e889..32744fff4 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -48,6 +48,7 @@ import LocalConfig from 'assets/config'; import telemetry from 'app/telemetry'; import App from './app'; +import EphemeralStore from 'app/store/ephemeral_store'; import './fetch_preconfig'; const PROMPT_IN_APP_PIN_CODE_AFTER = 5 * 60 * 1000; @@ -448,6 +449,14 @@ const launchEntry = () => { 'start:channel_screen', ]); + // Keep track of the latest componentId to appear and disappear + Navigation.events().registerComponentDidAppearListener(({componentId}) => { + EphemeralStore.addComponentIdToStack(componentId); + }); + Navigation.events().registerComponentDidDisappearListener(({componentId}) => { + EphemeralStore.removeComponentIdFromStack(componentId); + }); + Navigation.setRoot({ root: { stack: { diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js index 05d67f9ca..700de6ab7 100644 --- a/app/screens/channel/channel.ios.js +++ b/app/screens/channel/channel.ios.js @@ -26,10 +26,10 @@ const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange'; export default class ChannelIOS extends ChannelBase { previewChannel = (passProps, options) => { - const {actions, componentId} = this.props; + const {actions} = this.props; const screen = 'ChannelPeek'; - actions.peek(componentId, screen, passProps, options); + actions.peek(screen, passProps, options); }; optionalProps = {previewChannel: this.previewChannel}; diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js index a57dccac1..c0768a3a5 100644 --- a/app/screens/client_upgrade/client_upgrade.js +++ b/app/screens/client_upgrade/client_upgrade.js @@ -26,6 +26,8 @@ export default class ClientUpgrade extends PureComponent { actions: PropTypes.shape({ logError: PropTypes.func.isRequired, setLastUpgradeCheck: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, currentVersion: PropTypes.string, @@ -66,7 +68,7 @@ export default class ClientUpgrade extends PureComponent { navigationButtonPressed({buttonId}) { if (buttonId === 'close-upgrade') { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); } } @@ -95,15 +97,15 @@ export default class ClientUpgrade extends PureComponent { const { closeAction, userCheckedForUpgrade, - componentId, + actions, } = this.props; if (closeAction) { closeAction(); } else if (userCheckedForUpgrade) { - Navigation.pop(componentId); + actions.popTopScreen(); } else { - Navigation.dismissModal(componentId); + actions.dismissModal(); } }; diff --git a/app/screens/client_upgrade/index.js b/app/screens/client_upgrade/index.js index b0984934c..14f67f586 100644 --- a/app/screens/client_upgrade/index.js +++ b/app/screens/client_upgrade/index.js @@ -5,6 +5,7 @@ import {connect} from 'react-redux'; import {logError} from 'mattermost-redux/actions/errors'; +import {popTopScreen, dismissModal} from 'app/actions/navigation'; import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade'; import getClientUpgrade from 'app/selectors/client_upgrade'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; @@ -29,6 +30,8 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ logError, setLastUpgradeCheck, + popTopScreen, + dismissModal, }, dispatch), }; } diff --git a/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap b/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap index 9c8aa9c0e..2830f2a22 100644 --- a/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap +++ b/app/screens/edit_profile/__snapshots__/edit_profile.test.js.snap @@ -26,34 +26,6 @@ exports[`edit_profile should match snapshot 1`] = ` } } maxFileSize={20971520} - navigator={ - Object { - "dismissModal": [MockFunction], - "push": [MockFunction], - "setButtons": [MockFunction] { - "calls": Array [ - Array [ - Object { - "rightButtons": Array [ - Object { - "disabled": true, - "id": "update-profile", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } onShowFileSizeWarning={[Function]} onShowUnsupportedMimeTypeWarning={[Function]} removeProfileImage={[Function]} diff --git a/app/screens/edit_profile/edit_profile.js b/app/screens/edit_profile/edit_profile.js index 0beb7c1da..640da9921 100644 --- a/app/screens/edit_profile/edit_profile.js +++ b/app/screens/edit_profile/edit_profile.js @@ -88,11 +88,13 @@ export default class EditProfile extends PureComponent { setProfileImageUri: PropTypes.func.isRequired, removeProfileImage: PropTypes.func.isRequired, updateUser: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, }).isRequired, currentUser: PropTypes.object.isRequired, firstNameDisabled: PropTypes.bool.isRequired, lastNameDisabled: PropTypes.bool.isRequired, - navigator: PropTypes.object.isRequired, nicknameDisabled: PropTypes.bool.isRequired, positionDisabled: PropTypes.bool.isRequired, theme: PropTypes.object.isRequired, @@ -118,7 +120,7 @@ export default class EditProfile extends PureComponent { }; this.rightButton.title = context.intl.formatMessage({id: t('mobile.account.settings.save'), defaultMessage: 'Save'}); - props.navigator.setButtons(buttons); + props.actions.setButtons(buttons); this.state = { email, @@ -176,12 +178,11 @@ export default class EditProfile extends PureComponent { }; close = () => { - if (this.props.commandType === 'Push') { - this.props.navigator.pop(); + const {commandType, actions} = this.props; + if (commandType === 'Push') { + actions.popTopScreen(); } else { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + actions.dismissModal(); } }; @@ -190,7 +191,7 @@ export default class EditProfile extends PureComponent { rightButtons: [{...this.rightButton, disabled: !enabled}], }; - this.props.navigator.setButtons(buttons); + this.props.actions.setButtons(buttons); }; handleRequestError = (error) => { @@ -254,9 +255,7 @@ export default class EditProfile extends PureComponent { handleRemoveProfileImage = () => { this.setState({profileImageRemove: true}); this.emitCanUpdateAccount(true); - this.props.navigator.dismissModal({ - animationType: 'none', - }); + this.props.actions.dismissModal(); } uploadProfileImage = async () => { @@ -500,7 +499,6 @@ export default class EditProfile extends PureComponent { const { currentUser, theme, - navigator, } = this.props; const { @@ -521,7 +519,6 @@ export default class EditProfile extends PureComponent { canTakeVideo={false} canBrowseVideoLibrary={false} maxFileSize={MAX_SIZE} - navigator={navigator} wrapper={true} uploadFiles={this.handleUploadProfileImage} removeProfileImage={this.handleRemoveProfileImage} diff --git a/app/screens/edit_profile/edit_profile.test.js b/app/screens/edit_profile/edit_profile.test.js index 98efa50a2..71bd67850 100644 --- a/app/screens/edit_profile/edit_profile.test.js +++ b/app/screens/edit_profile/edit_profile.test.js @@ -17,16 +17,13 @@ jest.mock('app/utils/theme', () => { }); describe('edit_profile', () => { - const navigator = { - setButtons: jest.fn(), - dismissModal: jest.fn(), - push: jest.fn(), - }; - const actions = { updateUser: jest.fn(), setProfileImageUri: jest.fn(), removeProfileImage: jest.fn(), + popTopScreen: jest.fn(), + dismissModal: jest.fn(), + setButtons: jest.fn(), }; const baseProps = { @@ -36,7 +33,6 @@ describe('edit_profile', () => { nicknameDisabled: true, positionDisabled: true, theme: Preferences.THEMES.default, - navigator, currentUser: { first_name: 'Dwight', last_name: 'Schrute', @@ -58,14 +54,9 @@ describe('edit_profile', () => { }); test('should match state on handleRemoveProfileImage', () => { - const newNavigator = { - dismissModal: jest.fn(), - setButtons: jest.fn(), - }; const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, ); @@ -79,7 +70,6 @@ describe('edit_profile', () => { expect(instance.emitCanUpdateAccount).toHaveBeenCalledTimes(1); expect(instance.emitCanUpdateAccount).toBeCalledWith(true); - expect(newNavigator.dismissModal).toHaveBeenCalledTimes(1); - expect(newNavigator.dismissModal).toBeCalledWith({animationType: 'none'}); + expect(baseProps.actions.dismissModal).toHaveBeenCalledTimes(1); }); }); diff --git a/app/screens/edit_profile/index.js b/app/screens/edit_profile/index.js index 44ee8b27f..81a29cb97 100644 --- a/app/screens/edit_profile/index.js +++ b/app/screens/edit_profile/index.js @@ -8,6 +8,7 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; +import {popTopScreen, dismissModal, setButtons} from 'app/actions/navigation'; import {setProfileImageUri, removeProfileImage, updateUser} from 'app/actions/views/edit_profile'; import EditProfile from './edit_profile'; @@ -49,6 +50,9 @@ function mapDispatchToProps(dispatch) { setProfileImageUri, removeProfileImage, updateUser, + popTopScreen, + dismissModal, + setButtons, }, dispatch), }; } diff --git a/app/screens/flagged_posts/flagged_posts.js b/app/screens/flagged_posts/flagged_posts.js index ca5b27681..bb8a03e52 100644 --- a/app/screens/flagged_posts/flagged_posts.js +++ b/app/screens/flagged_posts/flagged_posts.js @@ -36,10 +36,11 @@ export default class FlaggedPosts extends PureComponent { selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, showSearchModal: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, didFail: PropTypes.bool, isLoading: PropTypes.bool, - navigator: PropTypes.object, postIds: PropTypes.array, theme: PropTypes.object.isRequired, }; @@ -65,38 +66,25 @@ export default class FlaggedPosts extends PureComponent { navigationButtonPressed({buttonId}) { if (buttonId === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); } } goToThread = (post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, + }; Keyboard.dismiss(); actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, - }; - - navigator.push(options); + actions.goToScreen(screen, title, passProps); }; handleClosePermalink = () => { @@ -111,11 +99,11 @@ export default class FlaggedPosts extends PureComponent { }; handleHashtagPress = async (hashtag) => { - const {actions, navigator} = this.props; + const {actions} = this.props; - await navigator.dismissModal(); + await actions.dismissModal(); - actions.showSearchModal(navigator, '#' + hashtag); + actions.showSearchModal('#' + hashtag); }; keyExtractor = (item) => item; @@ -169,7 +157,6 @@ export default class FlaggedPosts extends PureComponent { previewPost={this.previewPost} highlightPinnedOrFlagged={false} goToThread={this.goToThread} - navigator={this.props.navigator} onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={mattermostManaged.getCachedConfig()} @@ -183,29 +170,24 @@ export default class FlaggedPosts extends PureComponent { }; showPermalinkView = (postId, isPermalink) => { - const {actions, navigator} = this.props; + const {actions} = this.props; actions.selectFocusedPostId(postId); if (!this.showingPermalink) { + const screen = 'Permalink'; + const passProps = { + isPermalink, + onClose: this.handleClosePermalink, + }; const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink, - onClose: this.handleClosePermalink, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; this.showingPermalink = true; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); } }; diff --git a/app/screens/flagged_posts/index.js b/app/screens/flagged_posts/index.js index 16d3446f8..d48a92c54 100644 --- a/app/screens/flagged_posts/index.js +++ b/app/screens/flagged_posts/index.js @@ -9,8 +9,13 @@ import {clearSearch, getFlaggedPosts} from 'mattermost-redux/actions/search'; import {RequestStatus} from 'mattermost-redux/constants'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import { + dismissModal, + goToScreen, + showSearchModal, + showModalOverCurrentContext, +} from 'app/actions/navigation'; import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel'; -import {showSearchModal} from 'app/actions/views/search'; import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list'; import FlaggedPosts from './flagged_posts'; @@ -42,6 +47,9 @@ function mapDispatchToProps(dispatch) { selectFocusedPostId, selectPost, showSearchModal, + dismissModal, + goToScreen, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js index eda0cf7ca..94ace245b 100644 --- a/app/screens/interactive_dialog/interactive_dialog.js +++ b/app/screens/interactive_dialog/interactive_dialog.js @@ -17,7 +17,6 @@ import DialogElement from './dialog_element.js'; export default class InteractiveDialog extends PureComponent { static propTypes = { - componentId: PropTypes.string, url: PropTypes.string.isRequired, callbackId: PropTypes.string, elements: PropTypes.arrayOf(PropTypes.object).isRequired, diff --git a/app/screens/login/login.js b/app/screens/login/login.js index 047aaed56..0157f5567 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -45,7 +45,6 @@ export default class Login extends PureComponent { resetToChannel: PropTypes.func.isRequired, goToScreen: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string.isRequired, theme: PropTypes.object, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, @@ -95,12 +94,12 @@ export default class Login extends PureComponent { }; goToMfa = () => { - const {componentId, actions} = this.props; + const {actions} = this.props; const {intl} = this.context; const screen = 'MFA'; const title = intl.formatMessage({id: 'mobile.routes.mfa', defaultMessage: 'Multi-factor Authentication'}); - actions.goToScreen(componentId, screen, title); + actions.goToScreen(screen, title); }; blur = () => { @@ -287,12 +286,12 @@ export default class Login extends PureComponent { }; forgotPassword = () => { - const {actions, componentId} = this.props; + const {actions} = this.props; const {intl} = this.context; const screen = 'ForgotPassword'; const title = intl.formatMessage({id: 'password_form.title', defaultMessage: 'Password Reset'}); - actions.goToScreen(componentId, screen, title); + actions.goToScreen(screen, title); } render() { diff --git a/app/screens/login/login.test.js b/app/screens/login/login.test.js index 040221c12..3a3998d4f 100644 --- a/app/screens/login/login.test.js +++ b/app/screens/login/login.test.js @@ -24,7 +24,6 @@ describe('Login', () => { loginId: '', password: '', loginRequest: {}, - componentId: 'component-id', actions: { handleLoginIdChanged: jest.fn(), handlePasswordChanged: jest.fn(), @@ -129,7 +128,6 @@ describe('Login', () => { expect(baseProps.actions.goToScreen). toHaveBeenCalledWith( - baseProps.componentId, 'MFA', 'Multi-factor Authentication', ); @@ -141,7 +139,6 @@ describe('Login', () => { expect(baseProps.actions.goToScreen). toHaveBeenCalledWith( - baseProps.componentId, 'ForgotPassword', 'Password Reset', ); diff --git a/app/screens/login_options/index.js b/app/screens/login_options/index.js index 6df348adf..e29cd446a 100644 --- a/app/screens/login_options/index.js +++ b/app/screens/login_options/index.js @@ -4,11 +4,11 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {goToScreen} from 'app/actions/navigation'; - import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; +import {goToScreen} from 'app/actions/navigation'; + import LoginOptions from './login_options'; function mapStateToProps(state) { diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js index 8d4425bc0..9df26aa0f 100644 --- a/app/screens/login_options/login_options.js +++ b/app/screens/login_options/login_options.js @@ -28,7 +28,6 @@ export default class LoginOptions extends PureComponent { actions: PropTypes.shape({ goToScreen: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string.isRequired, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, }; @@ -46,21 +45,21 @@ export default class LoginOptions extends PureComponent { } goToLogin = preventDoubleTap(() => { - const {actions, componentId} = this.props; + const {actions} = this.props; const {intl} = this.context; const screen = 'Login'; const title = intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'}); - actions.goToScreen(componentId, screen, title); + actions.goToScreen(screen, title); }); goToSSO = (ssoType) => { - const {actions, componentId} = this.props; + const {actions} = this.props; const {intl} = this.context; const screen = 'SSO'; const title = intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'}); - actions.goToScreen(componentId, screen, title, {ssoType}); + actions.goToScreen(screen, title, {ssoType}); }; orientationDidChange = () => { diff --git a/app/screens/mfa/index.js b/app/screens/mfa/index.js index 4707c0bd2..4dea96eb2 100644 --- a/app/screens/mfa/index.js +++ b/app/screens/mfa/index.js @@ -6,6 +6,8 @@ import {connect} from 'react-redux'; import {login} from 'mattermost-redux/actions/users'; +import {popTopScreen} from 'app/actions/navigation'; + import Mfa from './mfa'; function mapStateToProps(state) { @@ -22,6 +24,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ login, + popTopScreen, }, dispatch), }; } diff --git a/app/screens/mfa/mfa.js b/app/screens/mfa/mfa.js index 81ef5f6f5..f530f783b 100644 --- a/app/screens/mfa/mfa.js +++ b/app/screens/mfa/mfa.js @@ -14,7 +14,6 @@ import { View, } from 'react-native'; import Button from 'react-native-button'; -import {Navigation} from 'react-native-navigation'; import {RequestStatus} from 'mattermost-redux/constants'; @@ -31,8 +30,8 @@ export default class Mfa extends PureComponent { static propTypes = { actions: PropTypes.shape({ login: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string.isRequired, loginId: PropTypes.string.isRequired, password: PropTypes.string.isRequired, loginRequest: PropTypes.object.isRequired, @@ -57,7 +56,7 @@ export default class Mfa extends PureComponent { // In case the login is successful the previous scene (login) will take care of the transition if (this.props.loginRequest.status === RequestStatus.STARTED && nextProps.loginRequest.status === RequestStatus.FAILURE) { - Navigation.pop(this.props.componentId); + this.props.actions.popTopScreen(); } } diff --git a/app/screens/options_modal/index.js b/app/screens/options_modal/index.js index 0c73b392b..75216f0f6 100644 --- a/app/screens/options_modal/index.js +++ b/app/screens/options_modal/index.js @@ -1,8 +1,11 @@ // 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 {dismissModal} from 'app/actions/navigation'; + import {getDimensions} from 'app/selectors/device'; import OptionsModal from './options_modal'; @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(OptionsModal); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + dismissModal, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(OptionsModal); diff --git a/app/screens/options_modal/options_modal.js b/app/screens/options_modal/options_modal.js index 062e8dac9..d39a62f29 100644 --- a/app/screens/options_modal/options_modal.js +++ b/app/screens/options_modal/options_modal.js @@ -9,7 +9,6 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; -import {Navigation} from 'react-native-navigation'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -23,7 +22,9 @@ const DURATION = 200; export default class OptionsModal extends PureComponent { static propTypes = { - componentId: PropTypes.string.isRequired, + actions: PropTypes.shape({ + dismissModal: PropTypes.func.isRequired, + }).isRequired, items: PropTypes.array.isRequired, deviceHeight: PropTypes.number.isRequired, deviceWidth: PropTypes.number.isRequired, @@ -68,12 +69,12 @@ export default class OptionsModal extends PureComponent { toValue: this.props.deviceHeight, duration: DURATION, }).start(() => { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); }); }; onItemPress = () => { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); } render() { diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js index 38b688174..d223b031b 100644 --- a/app/screens/permalink/permalink.js +++ b/app/screens/permalink/permalink.js @@ -58,7 +58,6 @@ export default class Permalink extends PureComponent { setChannelDisplayName: PropTypes.func.isRequired, setChannelLoading: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string, channelId: PropTypes.string, channelIsArchived: PropTypes.bool, channelName: PropTypes.string, diff --git a/app/screens/pinned_posts/pinned_posts.js b/app/screens/pinned_posts/pinned_posts.js index 899331ebb..2d36da00a 100644 --- a/app/screens/pinned_posts/pinned_posts.js +++ b/app/screens/pinned_posts/pinned_posts.js @@ -28,7 +28,6 @@ import noResultsImage from 'assets/images/no_results/pin.png'; export default class PinnedPosts extends PureComponent { static propTypes = { - componentId: PropTypes.string, actions: PropTypes.shape({ clearSearch: PropTypes.func.isRequired, loadChannelsByTeamName: PropTypes.func.isRequired, diff --git a/app/screens/reaction_list/reaction_list.js b/app/screens/reaction_list/reaction_list.js index 3e7189105..14a3a4787 100644 --- a/app/screens/reaction_list/reaction_list.js +++ b/app/screens/reaction_list/reaction_list.js @@ -29,7 +29,6 @@ export default class ReactionList extends PureComponent { actions: PropTypes.shape({ getMissingProfilesByIds: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string, navigator: PropTypes.object, reactions: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, diff --git a/app/screens/recent_mentions/index.js b/app/screens/recent_mentions/index.js index 914e756f2..7ee56b2ef 100644 --- a/app/screens/recent_mentions/index.js +++ b/app/screens/recent_mentions/index.js @@ -9,8 +9,13 @@ import {clearSearch, getRecentMentions} from 'mattermost-redux/actions/search'; import {RequestStatus} from 'mattermost-redux/constants'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import { + dismissModal, + goToScreen, + showSearchModal, + showModalOverCurrentContext, +} from 'app/actions/navigation'; import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel'; -import {showSearchModal} from 'app/actions/views/search'; import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list'; import RecentMentions from './recent_mentions'; @@ -42,6 +47,9 @@ function mapDispatchToProps(dispatch) { selectFocusedPostId, selectPost, showSearchModal, + dismissModal, + goToScreen, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/screens/recent_mentions/recent_mentions.js b/app/screens/recent_mentions/recent_mentions.js index e836194d9..9fd2ba7a1 100644 --- a/app/screens/recent_mentions/recent_mentions.js +++ b/app/screens/recent_mentions/recent_mentions.js @@ -36,10 +36,11 @@ export default class RecentMentions extends PureComponent { selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, showSearchModal: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, didFail: PropTypes.bool, isLoading: PropTypes.bool, - navigator: PropTypes.object, postIds: PropTypes.array, theme: PropTypes.object.isRequired, }; @@ -64,31 +65,20 @@ export default class RecentMentions extends PureComponent { } goToThread = (post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, + }; Keyboard.dismiss(); actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, - }; - - navigator.push(options); + actions.goToScreen(screen, title, passProps); }; handleClosePermalink = () => { @@ -103,20 +93,18 @@ export default class RecentMentions extends PureComponent { }; handleHashtagPress = async (hashtag) => { - const {actions, navigator} = this.props; + const {actions} = this.props; - await navigator.dismissModal(); + await actions.dismissModal(); - actions.showSearchModal(navigator, '#' + hashtag); + actions.showSearchModal('#' + hashtag); }; keyExtractor = (item) => item; navigationButtonPressed({buttonId}) { if (buttonId === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); } } @@ -168,7 +156,6 @@ export default class RecentMentions extends PureComponent { postId={item} previewPost={this.previewPost} goToThread={this.goToThread} - navigator={this.props.navigator} onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={mattermostManaged.getCachedConfig()} @@ -181,29 +168,24 @@ export default class RecentMentions extends PureComponent { }; showPermalinkView = (postId, isPermalink) => { - const {actions, navigator} = this.props; + const {actions} = this.props; actions.selectFocusedPostId(postId); if (!this.showingPermalink) { + const screen = 'Permalink'; + const passProps = { + isPermalink, + onClose: this.handleClosePermalink, + }; const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink, - onClose: this.handleClosePermalink, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; this.showingPermalink = true; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); } }; diff --git a/app/screens/search/index.js b/app/screens/search/index.js index c7194a20c..a8751a9f6 100644 --- a/app/screens/search/index.js +++ b/app/screens/search/index.js @@ -8,18 +8,19 @@ import {selectFocusedPostId, selectPost} from 'mattermost-redux/actions/posts'; import {clearSearch, removeSearchTerms, searchPostsWithParams, getMorePostsForSearch} from 'mattermost-redux/actions/search'; import {getCurrentChannelId, filterPostIds} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; +import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; +import {dismissModal} from 'app/actions/navigation'; import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel'; +import {handleSearchDraftChanged} from 'app/actions/views/search'; import {isLandscape} from 'app/selectors/device'; import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list'; -import {handleSearchDraftChanged} from 'app/actions/views/search'; import {getDeviceUtcOffset, getUtcOffsetForTimeZone} from 'app/utils/timezone'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; import Search from './search'; @@ -84,6 +85,7 @@ function mapDispatchToProps(dispatch) { searchPostsWithParams, getMorePostsForSearch, selectPost, + dismissModal, }, dispatch), }; } diff --git a/app/screens/search/search.js b/app/screens/search/search.js index 4bd53c82c..82acb7e3e 100644 --- a/app/screens/search/search.js +++ b/app/screens/search/search.js @@ -56,6 +56,7 @@ export default class Search extends PureComponent { getMorePostsForSearch: PropTypes.func.isRequired, selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string.isRequired, currentTeamId: PropTypes.string.isRequired, @@ -147,7 +148,7 @@ export default class Search extends PureComponent { if (this.state.preview) { this.refs.preview.handleClose(); } else { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); } } } @@ -178,7 +179,7 @@ export default class Search extends PureComponent { cancelSearch = preventDoubleTap(() => { this.handleTextChanged('', true); - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); }); goToThread = (post) => { @@ -211,7 +212,7 @@ export default class Search extends PureComponent { handleHashtagPress = (hashtag) => { if (this.showingPermalink) { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); this.handleClosePermalink(); } diff --git a/app/screens/search/search_result_post/search_result_post.js b/app/screens/search/search_result_post/search_result_post.js index 6e4481b1d..dc8e60261 100644 --- a/app/screens/search/search_result_post/search_result_post.js +++ b/app/screens/search/search_result_post/search_result_post.js @@ -12,7 +12,6 @@ export default class SearchResultPost extends PureComponent { goToThread: PropTypes.func.isRequired, highlightPinnedOrFlagged: PropTypes.bool, managedConfig: PropTypes.object.isRequired, - navigator: PropTypes.object.isRequired, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func.isRequired, postId: PropTypes.string.isRequired, @@ -50,7 +49,6 @@ export default class SearchResultPost extends PureComponent { isSearchResult={true} showAddReaction={false} showFullDate={this.props.showFullDate} - navigator={this.props.navigator} /> ); } diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index 16cb8e4b6..052044aa2 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -56,7 +56,6 @@ export default class SelectServer extends PureComponent { setServerVersion: PropTypes.func.isRequired, }).isRequired, allowOtherServers: PropTypes.bool, - componentId: PropTypes.string.isRequired, config: PropTypes.object, currentVersion: PropTypes.string, hasConfigAndLicense: PropTypes.bool.isRequired, @@ -158,7 +157,7 @@ export default class SelectServer extends PureComponent { }; goToNextScreen = (screen, title, passProps = {}, navOptions = {}) => { - const {actions, componentId} = this.props; + const {actions} = this.props; const defaultOptions = { popGesture: !LocalConfig.AutoSelectServerUrl, topBar: { @@ -168,7 +167,7 @@ export default class SelectServer extends PureComponent { }; const options = merge(defaultOptions, navOptions); - actions.goToScreen(componentId, screen, title, passProps, options); + actions.goToScreen(screen, title, passProps, options); }; handleAndroidKeyboard = () => { diff --git a/app/screens/select_team/index.js b/app/screens/select_team/index.js index 3cfe25e34..cfbe925f1 100644 --- a/app/screens/select_team/index.js +++ b/app/screens/select_team/index.js @@ -8,7 +8,7 @@ import {getTeams, joinTeam} from 'mattermost-redux/actions/teams'; import {logout} from 'mattermost-redux/actions/users'; import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams'; -import {resetToChannel} from 'app/actions/navigation'; +import {resetToChannel, dismissModal} from 'app/actions/navigation'; import {handleTeamChange} from 'app/actions/views/select_team'; import SelectTeam from './select_team.js'; @@ -28,6 +28,7 @@ function mapDispatchToProps(dispatch) { joinTeam, logout, resetToChannel, + dismissModal, }, dispatch), }; } diff --git a/app/screens/select_team/select_team.js b/app/screens/select_team/select_team.js index 5b66520fb..2e0866aca 100644 --- a/app/screens/select_team/select_team.js +++ b/app/screens/select_team/select_team.js @@ -46,8 +46,9 @@ export default class SelectTeam extends PureComponent { joinTeam: PropTypes.func.isRequired, logout: PropTypes.func.isRequired, resetToChannel: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, - componentId: PropTypes.string, + componentId: PropTypes.string.isRequired, currentUrl: PropTypes.string.isRequired, userWithoutTeams: PropTypes.bool, teams: PropTypes.array.isRequired, @@ -124,7 +125,7 @@ export default class SelectTeam extends PureComponent { }; close = () => { - Navigation.dismissModal(this.props.componentId); + this.props.actions.dismissModal(); }; goToChannelView = () => { diff --git a/app/screens/select_team/select_team.test.js b/app/screens/select_team/select_team.test.js index 9d6adc9df..7e3a1219b 100644 --- a/app/screens/select_team/select_team.test.js +++ b/app/screens/select_team/select_team.test.js @@ -30,6 +30,7 @@ describe('SelectTeam', () => { joinTeam: jest.fn(), logout: jest.fn(), resetToChannel: jest.fn(), + dismissModal: jest.fn(), }; const baseProps = { diff --git a/app/screens/settings/display_settings/display_settings.js b/app/screens/settings/display_settings/display_settings.js index 24ffdca47..19d135d35 100644 --- a/app/screens/settings/display_settings/display_settings.js +++ b/app/screens/settings/display_settings/display_settings.js @@ -8,12 +8,11 @@ import { Platform, View, } from 'react-native'; -import {Navigation} from 'react-native-navigation'; import SettingsItem from 'app/screens/settings/settings_item'; import StatusBar from 'app/components/status_bar'; import {preventDoubleTap} from 'app/utils/tap'; -import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import ClockDisplay from 'app/screens/clock_display'; @@ -34,16 +33,6 @@ export default class DisplaySettings extends PureComponent { showClockDisplaySettings: false, }; - componentDidMount() { - this.navigationEventListener = Navigation.events().bindComponent(this); - } - - // TODO: Remove this once styles are passed in push call in - // app/screens/settings/general/settings.js - componentDidAppear() { - setNavigatorStyles(this.props.componentId, this.props.theme); - } - closeClockDisplaySettings = () => { this.setState({showClockDisplaySettings: false}); }; diff --git a/app/screens/settings/general/index.js b/app/screens/settings/general/index.js index ab8457df1..09cc293ab 100644 --- a/app/screens/settings/general/index.js +++ b/app/screens/settings/general/index.js @@ -7,9 +7,10 @@ import {connect} from 'react-redux'; import {clearErrors} from 'mattermost-redux/actions/errors'; import {getCurrentUrl, getConfig} from 'mattermost-redux/selectors/entities/general'; import {getJoinableTeams} from 'mattermost-redux/selectors/entities/teams'; - -import {purgeOfflineStore} from 'app/actions/views/root'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + +import {goToScreen, dismissModal} from 'app/actions/navigation'; +import {purgeOfflineStore} from 'app/actions/views/root'; import {removeProtocol} from 'app/utils/url'; import Settings from './settings'; @@ -33,6 +34,8 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ clearErrors, purgeOfflineStore, + goToScreen, + dismissModal, }, dispatch), }; } diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js index f1569a2fc..94cbdba27 100644 --- a/app/screens/settings/general/settings.js +++ b/app/screens/settings/general/settings.js @@ -16,7 +16,7 @@ import {Navigation} from 'react-native-navigation'; import SettingsItem from 'app/screens/settings/settings_item'; import StatusBar from 'app/components/status_bar'; import {preventDoubleTap} from 'app/utils/tap'; -import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import {isValidUrl} from 'app/utils/url'; import {t} from 'app/utils/i18n'; @@ -27,6 +27,8 @@ class Settings extends PureComponent { actions: PropTypes.shape({ clearErrors: PropTypes.func.isRequired, purgeOfflineStore: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, config: PropTypes.object.isRequired, @@ -36,7 +38,6 @@ class Settings extends PureComponent { errors: PropTypes.array.isRequired, intl: intlShape.isRequired, joinableTeams: PropTypes.array.isRequired, - navigator: PropTypes.object, theme: PropTypes.object, }; @@ -49,17 +50,9 @@ class Settings extends PureComponent { this.navigationEventListener = Navigation.events().bindComponent(this); } - // TODO: Remove this once styles are passed in push/showModal call in - // app/components/sidebars/settings/settings_sidebar.js - componentDidAppear() { - setNavigatorStyles(this.props.componentId, this.props.theme); - } - navigationButtonPressed({buttonId}) { if (buttonId === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); } } @@ -90,111 +83,58 @@ class Settings extends PureComponent { }; goToAbout = preventDoubleTap(() => { - const {intl, navigator, theme, config} = this.props; - navigator.push({ - screen: 'About', - title: intl.formatMessage({id: 'about.title', defaultMessage: 'About {appTitle}'}, {appTitle: config.SiteName || 'Mattermost'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - }, - }); + const {actions, intl, config} = this.props; + const screen = 'About'; + const title = intl.formatMessage({id: 'about.title', defaultMessage: 'About {appTitle}'}, {appTitle: config.SiteName || 'Mattermost'}); + + actions.goToScreen(screen, title); }); goToNotifications = preventDoubleTap(() => { - const {intl, navigator, theme} = this.props; - navigator.push({ - screen: 'NotificationSettings', - backButtonTitle: '', - title: intl.formatMessage({id: 'user.settings.modal.notifications', defaultMessage: 'Notifications'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const {actions, intl} = this.props; + const screen = 'NotificationSettings'; + const title = intl.formatMessage({id: 'user.settings.modal.notifications', defaultMessage: 'Notifications'}); + + actions.goToScreen(screen, title); }); goToDisplaySettings = preventDoubleTap(() => { - const {intl, navigator, theme} = this.props; + const {actions, intl} = this.props; + const screen = 'DisplaySettings'; + const title = intl.formatMessage({id: 'user.settings.modal.display', defaultMessage: 'Display'}); - // TODO: Ensure all styles used in app/utils/theme's setNavigatorStyles - // are passed to DisplaySettings when this push call is updated to RNN v2 - // then remove setNavigatorStyles call in app/screens/settings/display_settings/display_settings.js - navigator.push({ - screen: 'DisplaySettings', - title: intl.formatMessage({id: 'user.settings.modal.display', defaultMessage: 'Display'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); goToAdvancedSettings = preventDoubleTap(() => { - const {intl, navigator, theme} = this.props; - navigator.push({ - screen: 'AdvancedSettings', - title: intl.formatMessage({id: 'mobile.advanced_settings.title', defaultMessage: 'Advanced Settings'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const {actions, intl} = this.props; + const screen = 'AdvancedSettings'; + const title = intl.formatMessage({id: 'mobile.advanced_settings.title', defaultMessage: 'Advanced Settings'}); + + actions.goToScreen(screen, title); }); goToSelectTeam = preventDoubleTap(() => { - const {currentUrl, intl, navigator, theme} = this.props; + const {actions, currentUrl, intl, theme} = this.props; + const screen = 'SelectTeam'; + const title = intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}); + const passProps = { + currentUrl, + theme, + }; - navigator.push({ - screen: 'SelectTeam', - title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUrl, - theme, - }, - }); + actions.goToScreen(screen, title, passProps); }); goToClientUpgrade = preventDoubleTap(() => { - const {intl, theme} = this.props; + const {actions, intl} = this.props; + const screen = 'ClientUpgrade'; + const title = intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'}); + const passProps = { + userCheckedForUpgrade: true, + }; - this.props.navigator.push({ - screen: 'ClientUpgrade', - title: intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarHidden: false, - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - }, - passProps: { - userCheckedForUpgrade: true, - }, - }); + actions.goToScreen(screen, title, passProps); }); openErrorEmail = preventDoubleTap(() => { diff --git a/app/store/ephemeral_store.js b/app/store/ephemeral_store.js new file mode 100644 index 000000000..7e69decae --- /dev/null +++ b/app/store/ephemeral_store.js @@ -0,0 +1,22 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +class EphemeralStore { + constructor() { + this.componentIdStack = []; + } + + getTopComponentId = () => this.componentIdStack[0]; + + addComponentIdToStack = (componentId) => { + this.componentIdStack.unshift(componentId); + } + + removeComponentIdFromStack = (componentId) => { + this.componentIdStack = this.componentIdStack.filter((id) => { + return id !== componentId; + }); + } +} + +export default new EphemeralStore(); diff --git a/app/utils/images.js b/app/utils/images.js index 2d0ab6771..c231e2ca2 100644 --- a/app/utils/images.js +++ b/app/utils/images.js @@ -57,44 +57,27 @@ export const calculateDimensions = (height, width, viewPortWidth = 0, viewPortHe }; }; -export function previewImageAtIndex(navigator, components, index, files) { +export function previewImageAtIndex(components, index, files, showModalOverCurrentContext) { previewComponents = components; const component = components[index]; if (component) { component.measure((rx, ry, width, height, x, y) => { - goToImagePreview( - navigator, - { + Keyboard.dismiss(); + requestAnimationFrame(() => { + const screen = 'ImagePreview'; + const passProps = { index, origin: {x, y, width, height}, target: {x: 0, y: 0, opacity: 1}, files, getItemMeasures, - } - ); + }; + showModalOverCurrentContext(screen, passProps); + }); }); } } -function goToImagePreview(navigator, passProps) { - Keyboard.dismiss(); - requestAnimationFrame(() => { - navigator.showModal({ - screen: 'ImagePreview', - title: '', - animationType: 'none', - passProps, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - }); - }); -} - function getItemMeasures(index, cb) { const activeComponent = previewComponents[index]; From 863043e12543a0c0c28d61483435f6350e49630a Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 25 Jun 2019 06:34:26 -0700 Subject: [PATCH 10/19] [MM-16134] Update notification settings related screens (#2914) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Remove navigationComponentId --- .../settings/notification_settings/index.js | 3 + .../notification_settings.js | 117 +++++++----------- .../notification_settings_auto_responder.js | 6 - .../notification_settings_mentions/index.js | 13 +- .../notification_settings_mention_base.js | 6 +- .../notification_settings_mentions.ios.js | 26 ++-- .../index.js | 13 +- ...notification_settings_mentions_keywords.js | 6 +- .../notification_settings_mobile_base.js | 1 - 9 files changed, 89 insertions(+), 102 deletions(-) diff --git a/app/screens/settings/notification_settings/index.js b/app/screens/settings/notification_settings/index.js index 4dd02c312..0196b84d1 100644 --- a/app/screens/settings/notification_settings/index.js +++ b/app/screens/settings/notification_settings/index.js @@ -11,6 +11,8 @@ import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; import {updateMe} from 'mattermost-redux/actions/users'; +import {goToScreen} from 'app/actions/navigation'; + import NotificationSettings from './notification_settings'; function mapStateToProps(state) { @@ -35,6 +37,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ updateMe, + goToScreen, }, dispatch), }; } diff --git a/app/screens/settings/notification_settings/notification_settings.js b/app/screens/settings/notification_settings/notification_settings.js index 2d269a132..47bd57e12 100644 --- a/app/screens/settings/notification_settings/notification_settings.js +++ b/app/screens/settings/notification_settings/notification_settings.js @@ -26,11 +26,11 @@ class NotificationSettings extends PureComponent { static propTypes = { actions: PropTypes.shape({ updateMe: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }), componentId: PropTypes.string, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, updateMeRequest: PropTypes.object.isRequired, currentUserStatus: PropTypes.string.isRequired, @@ -62,92 +62,65 @@ class NotificationSettings extends PureComponent { }); goToNotificationSettingsAutoResponder = () => { - const {currentUser, intl, navigator, theme} = this.props; - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsAutoResponder', - title: intl.formatMessage({ - id: 'mobile.notification_settings.auto_responder_short', - defaultMessage: 'Automatic Replies', - }), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUser, - onBack: this.saveAutoResponder, - }, + const {actions, currentUser, intl} = this.props; + const screen = 'NotificationSettingsAutoResponder'; + const title = intl.formatMessage({ + id: 'mobile.notification_settings.auto_responder_short', + defaultMessage: 'Automatic Replies', }); + const passProps = { + currentUser, + onBack: this.saveAutoResponder, + }; + + actions.goToScreen(screen, title, passProps); }; goToNotificationSettingsEmail = () => { - const {currentUser, intl, navigator, theme} = this.props; - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsEmail', - title: intl.formatMessage({ - id: 'mobile.notification_settings.email_title', - defaultMessage: 'Email Notifications', - }), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUser, - }, + const {actions, currentUser, intl} = this.props; + const screen = 'NotificationSettingsEmail'; + const title = intl.formatMessage({ + id: 'mobile.notification_settings.email_title', + defaultMessage: 'Email Notifications', }); + const passProps = { + currentUser, + }; + + actions.goToScreen(screen, title, passProps); }; goToNotificationSettingsMentions = () => { - const {currentUser, intl, navigator, theme} = this.props; - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsMentions', - title: intl.formatMessage({id: 'mobile.notification_settings.mentions_replies', defaultMessage: 'Mentions and Replies'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUser, - onBack: this.saveNotificationProps, - }, + const {actions, currentUser, intl} = this.props; + const screen = 'NotificationSettingsMentions'; + const title = intl.formatMessage({ + id: 'mobile.notification_settings.mentions_replies', + defaultMessage: 'Mentions and Replies', }); + const passProps = { + currentUser, + onBack: this.saveNotificationProps, + }; + + actions.goToScreen(screen, title, passProps); }; goToNotificationSettingsMobile = () => { - const {currentUser, intl, navigator, theme} = this.props; + const {actions, currentUser, intl} = this.props; + const screen = 'NotificationSettingsMobile'; + const title = intl.formatMessage({ + id: 'mobile.notification_settings.mobile_title', + defaultMessage: 'Mobile Notifications', + }); NotificationPreferences.getPreferences().then((notificationPreferences) => { + const passProps = { + currentUser, + onBack: this.saveNotificationProps, + notificationPreferences, + }; requestAnimationFrame(() => { - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsMobile', - title: intl.formatMessage({id: 'mobile.notification_settings.mobile_title', defaultMessage: 'Mobile Notifications'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUser, - onBack: this.saveNotificationProps, - notificationPreferences, - }, - }); + actions.goToScreen(screen, title, passProps); }); }).catch((e) => { Alert.alert('There was a problem getting the device preferences', e.message); diff --git a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js index 3f7cbb5cf..9bf28d12d 100644 --- a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js +++ b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js @@ -7,7 +7,6 @@ import PropTypes from 'prop-types'; import { View, } from 'react-native'; -import {Navigation} from 'react-native-navigation'; import {intlShape} from 'react-intl'; import {General} from 'mattermost-redux/constants'; @@ -25,7 +24,6 @@ import SectionItem from 'app/screens/settings/section_item'; export default class NotificationSettingsAutoResponder extends PureComponent { static propTypes = { currentUser: PropTypes.object.isRequired, - navigator: PropTypes.object, onBack: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, currentUserStatus: PropTypes.string.isRequired, @@ -58,10 +56,6 @@ export default class NotificationSettingsAutoResponder extends PureComponent { }; } - componentDidMount() { - this.navigationEventListener = Navigation.events().bindComponent(this); - } - componentWillUnmount() { this.saveUserNotifyProps(); } diff --git a/app/screens/settings/notification_settings_mentions/index.js b/app/screens/settings/notification_settings_mentions/index.js index 74b423fde..5a4083b5f 100644 --- a/app/screens/settings/notification_settings_mentions/index.js +++ b/app/screens/settings/notification_settings_mentions/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import NotificationSettingsMentions from './notification_settings_mentions'; function mapStateToProps(state) { @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(NotificationSettingsMentions); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettingsMentions); diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js index b7a976468..d2fced3eb 100644 --- a/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js +++ b/app/screens/settings/notification_settings_mentions/notification_settings_mention_base.js @@ -11,10 +11,12 @@ import {setNavigatorStyles} from 'app/utils/theme'; export default class NotificationSettingsMentionsBase extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, - navigator: PropTypes.object, onBack: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, }; @@ -29,7 +31,7 @@ export default class NotificationSettingsMentionsBase extends PureComponent { const {currentUser} = props; const notifyProps = getNotificationProps(currentUser); - this.goingBack = true; //use to identify if the navigator is popping this screen + this.goingBack = true; // use to identify if the navigator is popping this screen this.state = this.setStateFromNotifyProps(notifyProps); } diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js index a82914bb2..681996552 100644 --- a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js +++ b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.ios.js @@ -20,25 +20,17 @@ import NotificationSettingsMentionsBase from './notification_settings_mention_ba class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase { goToNotificationSettingsMentionKeywords = () => { - const {intl, navigator, theme} = this.props; + const {actions, intl} = this.props; this.goingBack = false; - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsMentionsKeywords', - title: intl.formatMessage({id: 'mobile.notification_settings_mentions.keywords', defaultMessage: 'Keywords'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - keywords: this.state.mention_keys, - onBack: this.updateMentionKeys, - }, - }); + const screen = 'NotificationSettingsMentionsKeywords'; + const title = intl.formatMessage({id: 'mobile.notification_settings_mentions.keywords', defaultMessage: 'Keywords'}); + const passProps = { + keywords: this.state.mention_keys, + onBack: this.updateMentionKeys, + }; + + actions.goToScreen(screen, title, passProps); }; renderMentionSection(style) { diff --git a/app/screens/settings/notification_settings_mentions_keywords/index.js b/app/screens/settings/notification_settings_mentions_keywords/index.js index a27f32151..f3a886bb0 100644 --- a/app/screens/settings/notification_settings_mentions_keywords/index.js +++ b/app/screens/settings/notification_settings_mentions_keywords/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {popTopScreen} from 'app/actions/navigation'; + import NotificationSettingsMentionsKeywords from './notification_settings_mentions_keywords'; function mapStateToProps(state) { @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(NotificationSettingsMentionsKeywords); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + popTopScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettingsMentionsKeywords); diff --git a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js index 77e9bd541..f6ab36b92 100644 --- a/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js +++ b/app/screens/settings/notification_settings_mentions_keywords/notification_settings_mentions_keywords.js @@ -12,9 +12,11 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class NotificationSettingsMentionsKeywords extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + popTopScreen: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string, keywords: PropTypes.string, - navigator: PropTypes.object, onBack: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, }; @@ -38,7 +40,7 @@ export default class NotificationSettingsMentionsKeywords extends PureComponent } handleSubmit = () => { - this.props.navigator.pop(); + this.props.actions.popTopScreen(); }; keywordsRef = (ref) => { diff --git a/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js b/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js index 2d3bd3c21..9eedfc43a 100644 --- a/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js +++ b/app/screens/settings/notification_settings_mobile/notification_settings_mobile_base.js @@ -19,7 +19,6 @@ export default class NotificationSettingsMobileBase extends PureComponent { config: PropTypes.object.isRequired, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, - navigator: PropTypes.object, notificationPreferences: PropTypes.object, onBack: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, From 74c7805e651886e631705676cd7e1e670d35841e Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 25 Jun 2019 09:06:00 -0700 Subject: [PATCH 11/19] [MM-15994] Revert change to use RNN v2's npm script (#2922) * Revert change to use RNN v2's npm script * Remove RNN v2 npm script --- Makefile | 12 ++++++++++-- package.json | 3 +-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 186cca33d..c75f80686 100644 --- a/Makefile +++ b/Makefile @@ -160,11 +160,19 @@ run-android: | check-device-android pre-run prepare-android-build ## Runs the ap @if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \ echo Starting React Native packager server; \ npm start & echo Running Android app in development; \ - npm run android; \ + if [ ! -z ${VARIANT} ]; then \ + react-native run-android --no-packager --variant=${VARIANT}; \ + else \ + react-native run-android --no-packager; \ + fi; \ wait; \ else \ echo Running Android app in development; \ - npm run android; \ + if [ ! -z ${VARIANT} ]; then \ + react-native run-android --no-packager --variant=${VARIANT}; \ + else \ + react-native run-android --no-packager; \ + fi; \ fi build: | stop pre-build check-style i18n-extract-ci ## Builds the app for Android & iOS diff --git a/package.json b/package.json index 45878152d..9cdb3d4cd 100644 --- a/package.json +++ b/package.json @@ -116,8 +116,7 @@ "test:watch": "jest --watch", "test:coverage": "jest --coverage", "updatesnapshot": "jest --updateSnapshot", - "mmjstool": "mmjstool", - "android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug" + "mmjstool": "mmjstool" }, "rnpm": { "assets": [ From b65ce5d283eb0e0d25819c85cfa075ffd06eeb03 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Wed, 26 Jun 2019 13:46:05 -0700 Subject: [PATCH 12/19] [MM-16136] Update channel related screens (#2915) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Update CreateChannel and children * Update MoreChannels * Update ChannelPeek and children * Update ChannelNavBar and children * Update ChannelPostList and children * Update ChannelInfo and children * Update ChannelAddMembers * Update ChannelMembers * Update EditChannel * Fix setting of top bar buttons * Update Channel screen and children * Fix typo * Use goToScreen for Android too * Remove navigationComponentId --- app/actions/navigation.js | 4 +- .../announcement_banner.js | 27 +- .../announcement_banner.test.js | 4 +- app/components/announcement_banner/index.js | 13 +- app/components/channel_intro/channel_intro.js | 31 +- app/components/channel_intro/index.js | 12 +- .../edit_channel_info/edit_channel_info.js | 415 +++++++++++++++++ app/components/edit_channel_info/index.js | 419 +----------------- .../interactive_dialog_controller/index.js | 13 +- .../interactive_dialog_controller.js | 41 +- app/components/post_list/index.js | 2 + app/components/post_list/post_list.js | 26 +- app/components/post_list/post_list.test.js | 6 +- app/screens/channel/channel_base.js | 85 ++-- .../channel_nav_bar/channel_nav_bar.js | 4 +- .../channel_search_button.js | 1 - .../channel_post_list/channel_post_list.js | 39 +- .../channel/channel_post_list/index.js | 2 + app/screens/channel/index.js | 4 +- .../channel_add_members.js | 16 +- .../channel_add_members.test.js | 19 +- app/screens/channel_add_members/index.js | 3 + app/screens/channel_info/channel_info.js | 125 ++---- .../channel_info/channel_info_header.js | 4 - app/screens/channel_info/index.js | 10 + .../channel_members/channel_members.js | 18 +- .../channel_members/channel_members.test.js | 8 +- app/screens/channel_members/index.js | 6 +- app/screens/channel_peek/channel_peek.js | 3 - app/screens/create_channel/create_channel.js | 29 +- app/screens/create_channel/index.js | 12 +- app/screens/edit_channel/edit_channel.js | 23 +- app/screens/edit_channel/index.js | 3 + app/screens/edit_profile/edit_profile.js | 12 +- app/screens/more_channels/index.js | 8 +- app/screens/more_channels/more_channels.js | 41 +- .../more_channels/more_channels.test.js | 24 +- 37 files changed, 738 insertions(+), 774 deletions(-) create mode 100644 app/components/edit_channel_info/edit_channel_info.js diff --git a/app/actions/navigation.js b/app/actions/navigation.js index 56eb887af..ca261c905 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -312,10 +312,8 @@ export function peek(name, passProps = {}, options = {}) { }; } -export function setButtons(buttons = {leftButtons: [], rightButtons: []}) { +export function setButtons(componentId, buttons = {leftButtons: [], rightButtons: []}) { return () => { - const componentId = EphemeralStore.getTopComponentId(); - Navigation.mergeOptions(componentId, { topBar: { ...buttons, diff --git a/app/components/announcement_banner/announcement_banner.js b/app/components/announcement_banner/announcement_banner.js index 3da97744b..67b4b6d91 100644 --- a/app/components/announcement_banner/announcement_banner.js +++ b/app/components/announcement_banner/announcement_banner.js @@ -18,12 +18,14 @@ const {View: AnimatedView} = Animated; export default class AnnouncementBanner extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, bannerColor: PropTypes.string, bannerDismissed: PropTypes.bool, bannerEnabled: PropTypes.bool, bannerText: PropTypes.string, bannerTextColor: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, }; @@ -52,23 +54,16 @@ export default class AnnouncementBanner extends PureComponent { } handlePress = () => { - const {navigator, theme} = this.props; + const {actions} = this.props; + const {intl} = this.context; - navigator.push({ - screen: 'ExpandedAnnouncementBanner', - title: this.context.intl.formatMessage({ - id: 'mobile.announcement_banner.title', - defaultMessage: 'Announcement', - }), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const screen = 'ExpandedAnnouncementBanner'; + const title = intl.formatMessage({ + id: 'mobile.announcement_banner.title', + defaultMessage: 'Announcement', }); + + actions.goToScreen(screen, title); }; toggleBanner = (show = true) => { diff --git a/app/components/announcement_banner/announcement_banner.test.js b/app/components/announcement_banner/announcement_banner.test.js index 5312962ba..73085ebf1 100644 --- a/app/components/announcement_banner/announcement_banner.test.js +++ b/app/components/announcement_banner/announcement_banner.test.js @@ -12,12 +12,14 @@ jest.useFakeTimers(); describe('AnnouncementBanner', () => { const baseProps = { + actions: { + goToScreen: jest.fn(), + }, bannerColor: '#ddd', bannerDismissed: false, bannerEnabled: true, bannerText: 'Banner Text', bannerTextColor: '#fff', - navigator: {}, theme: Preferences.THEMES.default, }; diff --git a/app/components/announcement_banner/index.js b/app/components/announcement_banner/index.js index f560c461d..f06d6eea1 100644 --- a/app/components/announcement_banner/index.js +++ b/app/components/announcement_banner/index.js @@ -1,11 +1,14 @@ // 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 {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; + import AnnouncementBanner from './announcement_banner'; function mapStateToProps(state) { @@ -23,4 +26,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(AnnouncementBanner); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(AnnouncementBanner); diff --git a/app/components/channel_intro/channel_intro.js b/app/components/channel_intro/channel_intro.js index d50e434d2..b9d0200ea 100644 --- a/app/components/channel_intro/channel_intro.js +++ b/app/components/channel_intro/channel_intro.js @@ -4,7 +4,6 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import { - Platform, Text, TouchableOpacity, View, @@ -21,11 +20,13 @@ import {t} from 'app/utils/i18n'; class ChannelIntro extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, creator: PropTypes.object, currentChannel: PropTypes.object.isRequired, currentChannelMembers: PropTypes.array.isRequired, intl: intlShape.isRequired, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, }; @@ -34,28 +35,14 @@ class ChannelIntro extends PureComponent { }; goToUserProfile = (userId) => { - const {intl, navigator, theme} = this.props; - const options = { - screen: 'UserProfile', - title: intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}), - animated: true, - backButtonTitle: '', - passProps: { - userId, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const {actions, intl} = this.props; + const screen = 'UserProfile'; + const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}); + const passProps = { + userId, }; - if (Platform.OS === 'ios') { - navigator.push(options); - } else { - navigator.showModal(options); - } + actions.goToScreen(screen, title, passProps); }; getDisplayName = (member) => { diff --git a/app/components/channel_intro/index.js b/app/components/channel_intro/index.js index 5495a4d01..8d6ccf49e 100644 --- a/app/components/channel_intro/index.js +++ b/app/components/channel_intro/index.js @@ -1,6 +1,7 @@ // 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 {createSelector} from 'reselect'; @@ -10,6 +11,7 @@ import {getCurrentUserId, getUser, makeGetProfilesInChannel} from 'mattermost-re import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; import {getChannelMembersForDm} from 'app/selectors/channel'; import ChannelIntro from './channel_intro'; @@ -52,4 +54,12 @@ function makeMapStateToProps() { }; } -export default connect(makeMapStateToProps)(ChannelIntro); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(makeMapStateToProps, mapDispatchToProps)(ChannelIntro); diff --git a/app/components/edit_channel_info/edit_channel_info.js b/app/components/edit_channel_info/edit_channel_info.js new file mode 100644 index 000000000..028240bdc --- /dev/null +++ b/app/components/edit_channel_info/edit_channel_info.js @@ -0,0 +1,415 @@ +// 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 { + Platform, + TouchableWithoutFeedback, + View, + Text, + findNodeHandle, +} from 'react-native'; +import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; + +import ErrorText from 'app/components/error_text'; +import FormattedText from 'app/components/formatted_text'; +import Loading from 'app/components/loading'; +import StatusBar from 'app/components/status_bar'; +import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; + +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; +import {General} from 'mattermost-redux/constants'; +import {getShortenedURL} from 'app/utils/url'; +import {t} from 'app/utils/i18n'; + +export default class EditChannelInfo extends PureComponent { + static propTypes = { + actions: PropTypes.shape({ + dismissModal: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, + }), + theme: PropTypes.object.isRequired, + deviceWidth: PropTypes.number.isRequired, + deviceHeight: PropTypes.number.isRequired, + channelType: PropTypes.string, + enableRightButton: PropTypes.func, + saving: PropTypes.bool.isRequired, + editing: PropTypes.bool, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + displayName: PropTypes.string, + currentTeamUrl: PropTypes.string, + channelURL: PropTypes.string, + purpose: PropTypes.string, + header: PropTypes.string, + onDisplayNameChange: PropTypes.func, + onChannelURLChange: PropTypes.func, + onPurposeChange: PropTypes.func, + onHeaderChange: PropTypes.func, + oldDisplayName: PropTypes.string, + oldChannelURL: PropTypes.string, + oldHeader: PropTypes.string, + oldPurpose: PropTypes.string, + }; + + static defaultProps = { + editing: false, + }; + + constructor(props) { + super(props); + + this.nameInput = React.createRef(); + this.urlInput = React.createRef(); + this.purposeInput = React.createRef(); + this.headerInput = React.createRef(); + this.lastText = React.createRef(); + this.scroll = React.createRef(); + } + + blur = () => { + if (this.nameInput?.current) { + this.nameInput.current.blur(); + } + + // TODO: uncomment below once the channel URL field is added + // if (this.urlInput?.current) { + // this.urlInput.current.blur(); + // } + + if (this.purposeInput?.current) { + this.purposeInput.current.blur(); + } + if (this.headerInput?.current) { + this.headerInput.current.blur(); + } + + if (this.scroll?.current) { + this.scroll.current.scrollToPosition(0, 0, true); + } + }; + + close = (goBack = false) => { + const {actions} = this.props; + if (goBack) { + actions.popTopScreen(); + } else { + actions.dismissModal(); + } + }; + + canUpdate = (displayName, channelURL, purpose, header) => { + const { + oldDisplayName, + oldChannelURL, + oldPurpose, + oldHeader, + } = this.props; + + return displayName !== oldDisplayName || channelURL !== oldChannelURL || + purpose !== oldPurpose || header !== oldHeader; + }; + + enableRightButton = (enable = false) => { + this.props.enableRightButton(enable); + }; + + onDisplayNameChangeText = (displayName) => { + const {editing, onDisplayNameChange} = this.props; + onDisplayNameChange(displayName); + + if (editing) { + const {channelURL, purpose, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + return; + } + + const displayNameExists = displayName && displayName.length >= 2; + this.props.enableRightButton(displayNameExists); + }; + + onDisplayURLChangeText = (channelURL) => { + const {editing, onChannelURLChange} = this.props; + onChannelURLChange(channelURL); + + if (editing) { + const {displayName, purpose, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + onPurposeChangeText = (purpose) => { + const {editing, onPurposeChange} = this.props; + onPurposeChange(purpose); + + if (editing) { + const {displayName, channelURL, header} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + onHeaderChangeText = (header) => { + const {editing, onHeaderChange} = this.props; + onHeaderChange(header); + + if (editing) { + const {displayName, channelURL, purpose} = this.props; + const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); + this.enableRightButton(canUpdate); + } + }; + + scrollToEnd = () => { + if (this.scroll?.current && this.lastText?.current) { + this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current)); + } + }; + + render() { + const { + theme, + editing, + channelType, + currentTeamUrl, + deviceWidth, + deviceHeight, + displayName, + channelURL, + header, + purpose, + } = this.props; + const {error, saving} = this.props; + const fullUrl = currentTeamUrl + '/channels'; + const shortUrl = getShortenedURL(fullUrl, 35); + + const style = getStyleSheet(theme); + + const displayHeaderOnly = channelType === General.DM_CHANNEL || + channelType === General.GM_CHANNEL; + + if (saving) { + return ( + + + + + ); + } + + let displayError; + if (error) { + displayError = ( + + + + + + ); + } + + return ( + + + + {displayError} + + + {!displayHeaderOnly && ( + + + + + + + + + )} + {/*TODO: Hide channel url field until it's added to CreateChannel */} + {false && editing && !displayHeaderOnly && ( + + + + + {shortUrl} + + + + + + + )} + {!displayHeaderOnly && ( + + + + + + + + + + + + + )} + + + + + + + + + + + + + + + ); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg, + }, + scrollView: { + flex: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), + paddingTop: 10, + }, + errorContainer: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), + }, + errorWrapper: { + justifyContent: 'center', + alignItems: 'center', + }, + inputContainer: { + marginTop: 10, + backgroundColor: '#fff', + }, + input: { + color: '#333', + fontSize: 14, + height: 40, + paddingHorizontal: 15, + }, + titleContainer30: { + flexDirection: 'row', + marginTop: 30, + }, + titleContainer15: { + flexDirection: 'row', + marginTop: 15, + }, + title: { + fontSize: 14, + color: theme.centerChannelColor, + marginLeft: 15, + }, + optional: { + color: changeOpacity(theme.centerChannelColor, 0.5), + fontSize: 14, + marginLeft: 5, + }, + helpText: { + fontSize: 14, + color: changeOpacity(theme.centerChannelColor, 0.5), + marginTop: 10, + marginHorizontal: 15, + }, + }; +}); diff --git a/app/components/edit_channel_info/index.js b/app/components/edit_channel_info/index.js index 66927a3f1..a523704f5 100644 --- a/app/components/edit_channel_info/index.js +++ b/app/components/edit_channel_info/index.js @@ -1,414 +1,23 @@ // 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 {bindActionCreators} from 'redux'; +import {connect} from 'react-redux'; + import { - Platform, - TouchableWithoutFeedback, - View, - Text, - findNodeHandle, -} from 'react-native'; -import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; + dismissModal, + popTopScreen, +} from 'app/actions/navigation'; -import ErrorText from 'app/components/error_text'; -import FormattedText from 'app/components/formatted_text'; -import Loading from 'app/components/loading'; -import StatusBar from 'app/components/status_bar'; -import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; +import EditChannelInfo from './edit_channel_info'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {General} from 'mattermost-redux/constants'; -import {getShortenedURL} from 'app/utils/url'; -import {t} from 'app/utils/i18n'; - -export default class EditChannelInfo extends PureComponent { - static propTypes = { - navigator: PropTypes.object.isRequired, - theme: PropTypes.object.isRequired, - deviceWidth: PropTypes.number.isRequired, - deviceHeight: PropTypes.number.isRequired, - channelType: PropTypes.string, - enableRightButton: PropTypes.func, - saving: PropTypes.bool.isRequired, - editing: PropTypes.bool, - error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - displayName: PropTypes.string, - currentTeamUrl: PropTypes.string, - channelURL: PropTypes.string, - purpose: PropTypes.string, - header: PropTypes.string, - onDisplayNameChange: PropTypes.func, - onChannelURLChange: PropTypes.func, - onPurposeChange: PropTypes.func, - onHeaderChange: PropTypes.func, - oldDisplayName: PropTypes.string, - oldChannelURL: PropTypes.string, - oldHeader: PropTypes.string, - oldPurpose: PropTypes.string, +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + dismissModal, + popTopScreen, + }, dispatch), }; - - static defaultProps = { - editing: false, - }; - - constructor(props) { - super(props); - - this.nameInput = React.createRef(); - this.urlInput = React.createRef(); - this.purposeInput = React.createRef(); - this.headerInput = React.createRef(); - this.lastText = React.createRef(); - this.scroll = React.createRef(); - } - - blur = () => { - if (this.nameInput?.current) { - this.nameInput.current.blur(); - } - - // TODO: uncomment below once the channel URL field is added - // if (this.urlInput?.current) { - // this.urlInput.current.blur(); - // } - - if (this.purposeInput?.current) { - this.purposeInput.current.blur(); - } - if (this.headerInput?.current) { - this.headerInput.current.blur(); - } - - if (this.scroll?.current) { - this.scroll.current.scrollToPosition(0, 0, true); - } - }; - - close = (goBack = false) => { - if (goBack) { - this.props.navigator.pop({animated: true}); - } else { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); - } - }; - - canUpdate = (displayName, channelURL, purpose, header) => { - const { - oldDisplayName, - oldChannelURL, - oldPurpose, - oldHeader, - } = this.props; - - return displayName !== oldDisplayName || channelURL !== oldChannelURL || - purpose !== oldPurpose || header !== oldHeader; - }; - - enableRightButton = (enable = false) => { - this.props.enableRightButton(enable); - }; - - onDisplayNameChangeText = (displayName) => { - const {editing, onDisplayNameChange} = this.props; - onDisplayNameChange(displayName); - - if (editing) { - const {channelURL, purpose, header} = this.props; - const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); - this.enableRightButton(canUpdate); - return; - } - - const displayNameExists = displayName && displayName.length >= 2; - this.props.enableRightButton(displayNameExists); - }; - - onDisplayURLChangeText = (channelURL) => { - const {editing, onChannelURLChange} = this.props; - onChannelURLChange(channelURL); - - if (editing) { - const {displayName, purpose, header} = this.props; - const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); - this.enableRightButton(canUpdate); - } - }; - - onPurposeChangeText = (purpose) => { - const {editing, onPurposeChange} = this.props; - onPurposeChange(purpose); - - if (editing) { - const {displayName, channelURL, header} = this.props; - const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); - this.enableRightButton(canUpdate); - } - }; - - onHeaderChangeText = (header) => { - const {editing, onHeaderChange} = this.props; - onHeaderChange(header); - - if (editing) { - const {displayName, channelURL, purpose} = this.props; - const canUpdate = this.canUpdate(displayName, channelURL, purpose, header); - this.enableRightButton(canUpdate); - } - }; - - scrollToEnd = () => { - if (this.scroll?.current && this.lastText?.current) { - this.scroll.current.scrollToFocusedInput(findNodeHandle(this.lastText.current)); - } - }; - - render() { - const { - theme, - editing, - channelType, - currentTeamUrl, - deviceWidth, - deviceHeight, - displayName, - channelURL, - header, - purpose, - } = this.props; - const {error, saving} = this.props; - const fullUrl = currentTeamUrl + '/channels'; - const shortUrl = getShortenedURL(fullUrl, 35); - - const style = getStyleSheet(theme); - - const displayHeaderOnly = channelType === General.DM_CHANNEL || - channelType === General.GM_CHANNEL; - - if (saving) { - return ( - - - - - ); - } - - let displayError; - if (error) { - displayError = ( - - - - - - ); - } - - return ( - - - - {displayError} - - - {!displayHeaderOnly && ( - - - - - - - - - )} - {/*TODO: Hide channel url field until it's added to CreateChannel */} - {false && editing && !displayHeaderOnly && ( - - - - - {shortUrl} - - - - - - - )} - {!displayHeaderOnly && ( - - - - - - - - - - - - - )} - - - - - - - - - - - - - - - ); - } } -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - return { - container: { - flex: 1, - backgroundColor: theme.centerChannelBg, - }, - scrollView: { - flex: 1, - backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), - paddingTop: 10, - }, - errorContainer: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), - }, - errorWrapper: { - justifyContent: 'center', - alignItems: 'center', - }, - inputContainer: { - marginTop: 10, - backgroundColor: '#fff', - }, - input: { - color: '#333', - fontSize: 14, - height: 40, - paddingHorizontal: 15, - }, - titleContainer30: { - flexDirection: 'row', - marginTop: 30, - }, - titleContainer15: { - flexDirection: 'row', - marginTop: 15, - }, - title: { - fontSize: 14, - color: theme.centerChannelColor, - marginLeft: 15, - }, - optional: { - color: changeOpacity(theme.centerChannelColor, 0.5), - fontSize: 14, - marginLeft: 5, - }, - helpText: { - fontSize: 14, - color: changeOpacity(theme.centerChannelColor, 0.5), - marginTop: 10, - marginHorizontal: 15, - }, - }; -}); - +export default connect(null, mapDispatchToProps)(EditChannelInfo); diff --git a/app/components/interactive_dialog_controller/index.js b/app/components/interactive_dialog_controller/index.js index 10d93d094..a2577835a 100644 --- a/app/components/interactive_dialog_controller/index.js +++ b/app/components/interactive_dialog_controller/index.js @@ -1,8 +1,11 @@ // 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 {showModal} from 'app/actions/navigation'; + import InteractiveDialogController from './interactive_dialog_controller'; function mapStateToProps(state) { @@ -12,4 +15,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(InteractiveDialogController); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showModal, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialogController); diff --git a/app/components/interactive_dialog_controller/interactive_dialog_controller.js b/app/components/interactive_dialog_controller/interactive_dialog_controller.js index fcba9b347..7ec4e0177 100644 --- a/app/components/interactive_dialog_controller/interactive_dialog_controller.js +++ b/app/components/interactive_dialog_controller/interactive_dialog_controller.js @@ -7,9 +7,11 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; export default class InteractiveDialogController extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + showModal: PropTypes.func.isRequired, + }).isRequired, triggerId: PropTypes.string, dialog: PropTypes.object, - navigator: PropTypes.object, theme: PropTypes.object, }; @@ -22,7 +24,7 @@ export default class InteractiveDialogController extends PureComponent { } componentDidUpdate(prevProps) { - const triggerId = this.props.triggerId; + const {actions, triggerId} = this.props; if (!triggerId) { return; } @@ -41,33 +43,24 @@ export default class InteractiveDialogController extends PureComponent { return; } - const theme = this.props.theme; - - this.props.navigator.showModal({ - backButtonTitle: '', - screen: 'InteractiveDialog', - title: dialogData.dialog.title, - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - navigatorButtons: { + const screen = 'InteractiveDialog'; + const title = dialogData.dialog.title; + const passProps = {}; + const options = { + topBar: { leftButtons: [{ id: 'close-dialog', icon: this.closeButton, }], - rightButtons: [ - { - id: 'submit-dialog', - showAsAction: 'always', - title: dialogData.dialog.submit_label, - }, - ], + rightButtons: [{ + id: 'submit-dialog', + showAsAction: 'always', + text: dialogData.dialog.submit_label, + }], }, - }); + }; + + actions.showModal(screen, title, passProps, options); } render() { diff --git a/app/components/post_list/index.js b/app/components/post_list/index.js index 92168b4ba..1c8f430ef 100644 --- a/app/components/post_list/index.js +++ b/app/components/post_list/index.js @@ -9,6 +9,7 @@ import {getConfig, getCurrentUrl} from 'mattermost-redux/selectors/entities/gene import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {makePreparePostIdsForPostList, START_OF_NEW_MESSAGES} from 'mattermost-redux/utils/post_list'; +import {showModalOverCurrentContext} from 'app/actions/navigation'; import {handleSelectChannelByName, loadChannelsByTeamName, refreshChannelWithRetry} from 'app/actions/views/channel'; import {setDeepLinkURL} from 'app/actions/views/root'; @@ -42,6 +43,7 @@ function mapDispatchToProps(dispatch) { refreshChannelWithRetry, selectFocusedPostId, setDeepLinkURL, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js index 3cc733f04..39e16dcac 100644 --- a/app/components/post_list/post_list.js +++ b/app/components/post_list/post_list.js @@ -41,6 +41,7 @@ export default class PostList extends PureComponent { refreshChannelWithRetry: PropTypes.func.isRequired, selectFocusedPostId: PropTypes.func.isRequired, setDeepLinkURL: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, channelId: PropTypes.string, deepLinkURL: PropTypes.string, @@ -51,7 +52,6 @@ export default class PostList extends PureComponent { isSearchResult: PropTypes.bool, lastPostIndex: PropTypes.number.isRequired, lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types - navigator: PropTypes.object, onLoadMoreUp: PropTypes.func, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, @@ -250,7 +250,6 @@ export default class PostList extends PureComponent { isSearchResult: this.props.isSearchResult, location: this.props.location, managedConfig: mattermostManaged.getCachedConfig(), - navigator: this.props.navigator, onHashtagPress: this.props.onHashtagPress, onPermalinkPress: this.handlePermalinkPress, onPress: this.props.onPostPress, @@ -303,29 +302,24 @@ export default class PostList extends PureComponent { }; showPermalinkView = (postId) => { - const {actions, navigator} = this.props; + const {actions} = this.props; actions.selectFocusedPostId(postId); if (!this.showingPermalink) { + const screen = 'Permalink'; + const passProps = { + isPermalink: true, + onClose: this.handleClosePermalink, + }; const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink: true, - onClose: this.handleClosePermalink, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; this.showingPermalink = true; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); } }; diff --git a/app/components/post_list/post_list.test.js b/app/components/post_list/post_list.test.js index 0c6455843..a4219bf89 100644 --- a/app/components/post_list/post_list.test.js +++ b/app/components/post_list/post_list.test.js @@ -18,11 +18,9 @@ describe('PostList', () => { refreshChannelWithRetry: jest.fn(), selectFocusedPostId: jest.fn(), setDeepLinkURL: jest.fn(), + showModalOverCurrentContext: jest.fn(), }, deepLinkURL: '', - navigator: { - showModal: jest.fn(), - }, lastPostIndex: -1, postIds: ['post-id-1', 'post-id-2'], serverURL, @@ -47,7 +45,7 @@ describe('PostList', () => { wrapper.setProps({deepLinkURL: deepLinks.permalink}); expect(baseProps.actions.setDeepLinkURL).toHaveBeenCalled(); expect(baseProps.actions.selectFocusedPostId).toHaveBeenCalled(); - expect(baseProps.navigator.showModal).toHaveBeenCalled(); + expect(baseProps.actions.showModalOverCurrentContext).toHaveBeenCalled(); expect(wrapper.getElement()).toMatchSnapshot(); }); diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js index 53e40b4ef..0a96f4250 100644 --- a/app/screens/channel/channel_base.js +++ b/app/screens/channel/channel_base.js @@ -6,12 +6,12 @@ import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import { Keyboard, - Platform, StyleSheet, View, } from 'react-native'; - +import {Navigation} from 'react-native-navigation'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; + import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {app} from 'app/mattermost'; @@ -40,12 +40,14 @@ export default class ChannelBase extends PureComponent { selectInitialChannel: PropTypes.func.isRequired, recordLoadTime: PropTypes.func.isRequired, peek: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string.isRequired, currentChannelId: PropTypes.string, channelsRequestFailed: PropTypes.bool, currentTeamId: PropTypes.string, isLandscape: PropTypes.bool, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, showTermsOfService: PropTypes.bool, disableTermsModal: PropTypes.bool, @@ -65,8 +67,10 @@ export default class ChannelBase extends PureComponent { this.postTextbox = React.createRef(); this.keyboardTracker = React.createRef(); - props.navigator.setStyle({ - screenBackgroundColor: props.theme.centerChannelBg, + Navigation.mergeOptions(props.componentId, { + layout: { + backgroundColor: props.theme.centerChannelBg, + }, }); if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) { @@ -104,8 +108,10 @@ export default class ChannelBase extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - this.props.navigator.setStyle({ - screenBackgroundColor: nextProps.theme.centerChannelBg, + Navigation.mergeOptions(this.props.componentId, { + layout: { + backgroundColor: nextProps.theme.centerChannelBg, + }, }); } @@ -161,53 +167,32 @@ export default class ChannelBase extends PureComponent { }; showTermsOfServiceModal = async () => { - const {navigator, theme} = this.props; + const {actions, theme} = this.props; const closeButton = await MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor); - navigator.showModal({ - screen: 'TermsOfService', - animationType: 'slide-up', - title: '', - backButtonTitle: '', - animated: true, - navigatorStyle: { - navBarTextColor: theme.centerChannelColor, - navBarBackgroundColor: theme.centerChannelBg, - navBarButtonColor: theme.buttonBg, - screenBackgroundColor: theme.centerChannelBg, - modalPresentationStyle: 'overCurrentContext', - }, - overrideBackPress: true, - passProps: { - closeButton, - }, - }); - }; - - goToChannelInfo = preventDoubleTap(() => { - const {intl} = this.context; - const {navigator, theme} = this.props; + const screen = 'TermsOfService'; + const passProps = { + closeButton, + }; const options = { - screen: 'ChannelInfo', - title: intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, + layout: { + backgroundColor: theme.centerChannelBg, }, }; + actions.showModalOverCurrentContext(screen, passProps, options); + }; + + goToChannelInfo = preventDoubleTap(() => { + const {actions} = this.props; + const {intl} = this.context; + const screen = 'ChannelInfo'; + const title = intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'}); + Keyboard.dismiss(); - if (Platform.OS === 'android') { - navigator.showModal(options); - } else { - requestAnimationFrame(() => { - navigator.push(options); - }); - } + requestAnimationFrame(() => { + actions.goToScreen(screen, title); + }); }); handleAutoComplete = (value) => { @@ -265,7 +250,6 @@ export default class ChannelBase extends PureComponent { channelsRequestFailed, currentChannelId, isLandscape, - navigator, theme, } = this.props; @@ -282,7 +266,7 @@ export default class ChannelBase extends PureComponent { const Loading = require('app/components/channel_loader').default; return ( - + {drawerContent} diff --git a/app/screens/channel/channel_nav_bar/channel_nav_bar.js b/app/screens/channel/channel_nav_bar/channel_nav_bar.js index d84a2cd6f..9e203e057 100644 --- a/app/screens/channel/channel_nav_bar/channel_nav_bar.js +++ b/app/screens/channel/channel_nav_bar/channel_nav_bar.js @@ -24,7 +24,6 @@ const { export default class ChannelNavBar extends PureComponent { static propTypes = { isLandscape: PropTypes.bool.isRequired, - navigator: PropTypes.object.isRequired, openChannelDrawer: PropTypes.func.isRequired, openSettingsDrawer: PropTypes.func.isRequired, onPress: PropTypes.func.isRequired, @@ -32,7 +31,7 @@ export default class ChannelNavBar extends PureComponent { }; render() { - const {isLandscape, navigator, onPress, theme} = this.props; + const {isLandscape, onPress, theme} = this.props; const {openChannelDrawer, openSettingsDrawer} = this.props; const style = getStyleFromTheme(theme); const padding = {paddingHorizontal: 0}; @@ -72,7 +71,6 @@ export default class ChannelNavBar extends PureComponent { /> diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js index c40a87411..554d14f55 100644 --- a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js +++ b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js @@ -19,7 +19,6 @@ export default class ChannelSearchButton extends PureComponent { clearSearch: PropTypes.func.isRequired, showSearchModal: PropTypes.func.isRequired, }).isRequired, - navigator: PropTypes.object, theme: PropTypes.object, }; diff --git a/app/screens/channel/channel_post_list/channel_post_list.js b/app/screens/channel/channel_post_list/channel_post_list.js index 26e2383a6..dc8ca68bc 100644 --- a/app/screens/channel/channel_post_list/channel_post_list.js +++ b/app/screens/channel/channel_post_list/channel_post_list.js @@ -33,13 +33,14 @@ export default class ChannelPostList extends PureComponent { selectPost: PropTypes.func.isRequired, recordLoadTime: PropTypes.func.isRequired, refreshChannelWithRetry: PropTypes.func.isRequired, + showModal: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, channelId: PropTypes.string.isRequired, channelRefreshingFailed: PropTypes.bool, currentUserId: PropTypes.string, lastViewedAt: PropTypes.number, loadMorePostsVisible: PropTypes.bool.isRequired, - navigator: PropTypes.object, postIds: PropTypes.array, postVisibility: PropTypes.number, refreshing: PropTypes.bool.isRequired, @@ -105,36 +106,23 @@ export default class ChannelPostList extends PureComponent { goToThread = (post) => { telemetry.start(['post_list:thread']); - const {actions, channelId, navigator, theme} = this.props; + const {actions, channelId} = this.props; const rootId = (post.root_id || post.id); Keyboard.dismiss(); actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, }; - if (Platform.OS === 'android') { - navigator.showModal(options); - } else { - requestAnimationFrame(() => { - navigator.push(options); - }); - } + requestAnimationFrame(() => { + actions.goToScreen(screen, title, passProps); + }); }; loadMorePostsTop = () => { @@ -181,7 +169,6 @@ export default class ChannelPostList extends PureComponent { return ( ); }; @@ -194,7 +181,6 @@ export default class ChannelPostList extends PureComponent { currentUserId, lastViewedAt, loadMorePostsVisible, - navigator, refreshing, theme, } = this.props; @@ -223,7 +209,6 @@ export default class ChannelPostList extends PureComponent { currentUserId={currentUserId} lastViewedAt={lastViewedAt} channelId={channelId} - navigator={navigator} renderFooter={this.renderFooter} refreshing={refreshing} scrollViewNativeID={channelId} @@ -234,7 +219,7 @@ export default class ChannelPostList extends PureComponent { return ( {component} - + ); diff --git a/app/screens/channel/channel_post_list/index.js b/app/screens/channel/channel_post_list/index.js index 2e34bdea1..dd5cd8368 100644 --- a/app/screens/channel/channel_post_list/index.js +++ b/app/screens/channel/channel_post_list/index.js @@ -10,6 +10,7 @@ import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels' import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {goToScreen} from 'app/actions/navigation'; import {loadPostsIfNecessaryWithRetry, loadThreadIfNecessary, increasePostVisibility, refreshChannelWithRetry} from 'app/actions/views/channel'; import {recordLoadTime} from 'app/actions/views/root'; @@ -42,6 +43,7 @@ function mapDispatchToProps(dispatch) { selectPost, recordLoadTime, refreshChannelWithRetry, + goToScreen, }, dispatch), }; } diff --git a/app/screens/channel/index.js b/app/screens/channel/index.js index 1c9fc4c26..e26fc7e13 100644 --- a/app/screens/channel/index.js +++ b/app/screens/channel/index.js @@ -19,7 +19,7 @@ import { import {connection} from 'app/actions/device'; import {recordLoadTime} from 'app/actions/views/root'; import {selectDefaultTeam} from 'app/actions/views/select_team'; -import {peek} from 'app/actions/navigation'; +import {peek, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation'; import {isLandscape} from 'app/selectors/device'; import Channel from './channel'; @@ -50,6 +50,8 @@ function mapDispatchToProps(dispatch) { startPeriodicStatusUpdates, stopPeriodicStatusUpdates, peek, + goToScreen, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/screens/channel_add_members/channel_add_members.js b/app/screens/channel_add_members/channel_add_members.js index 11e42ea88..95eb6010a 100644 --- a/app/screens/channel_add_members/channel_add_members.js +++ b/app/screens/channel_add_members/channel_add_members.js @@ -33,6 +33,8 @@ export default class ChannelAddMembers extends PureComponent { getProfilesNotInChannel: PropTypes.func.isRequired, handleAddChannelMembers: PropTypes.func.isRequired, searchProfiles: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, currentChannelId: PropTypes.string.isRequired, @@ -40,7 +42,6 @@ export default class ChannelAddMembers extends PureComponent { currentTeamId: PropTypes.string.isRequired, currentUserId: PropTypes.string.isRequired, profilesNotInChannel: PropTypes.array.isRequired, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, }; @@ -68,13 +69,13 @@ export default class ChannelAddMembers extends PureComponent { }; this.addButton = { - disabled: true, + enalbed: false, id: 'add-members', - title: context.intl.formatMessage({id: 'integrations.add', defaultMessage: 'Add'}), + text: context.intl.formatMessage({id: 'integrations.add', defaultMessage: 'Add'}), showAsAction: 'always', }; - props.navigator.setButtons({ + props.actions.setButtons(props.componentId, { rightButtons: [this.addButton], }); } @@ -113,12 +114,13 @@ export default class ChannelAddMembers extends PureComponent { }; close = () => { - this.props.navigator.pop({animated: true}); + this.props.actions.popTopScreen(); }; enableAddOption = (enabled) => { - this.props.navigator.setButtons({ - rightButtons: [{...this.addButton, disabled: !enabled}], + const {actions, componentId} = this.props; + actions.setButtons(componentId, { + rightButtons: [{...this.addButton, enabled}], }); }; diff --git a/app/screens/channel_add_members/channel_add_members.test.js b/app/screens/channel_add_members/channel_add_members.test.js index 3d5eaed74..0684d5685 100644 --- a/app/screens/channel_add_members/channel_add_members.test.js +++ b/app/screens/channel_add_members/channel_add_members.test.js @@ -16,15 +16,13 @@ describe('ChannelAddMembers', () => { getProfilesNotInChannel: jest.fn().mockResolvedValue({}), handleAddChannelMembers: jest.fn().mockResolvedValue({}), searchProfiles: jest.fn().mockResolvedValue({data: []}), + setButtons: jest.fn(), + popTopScreen: jest.fn(), }, currentChannelId: 'current_channel_id', currentTeamId: 'current_team_id', currentUserId: 'current_user_id', profilesNotInChannel: [], - navigator: { - pop: jest.fn(), - setButtons: jest.fn(), - }, theme: Preferences.THEMES.default, componentId: 'component-id', }; @@ -35,10 +33,10 @@ describe('ChannelAddMembers', () => { expect(baseProps.actions.getTeamStats).toBeCalledTimes(1); expect(baseProps.actions.getTeamStats).toBeCalledWith(baseProps.currentTeamId); - const button = {disabled: true, id: 'add-members', title: 'Add', showAsAction: 'always'}; - expect(baseProps.navigator.setButtons).toBeCalledTimes(2); - expect(baseProps.navigator.setButtons.mock.calls[0][0]).toEqual({rightButtons: [button]}); - expect(baseProps.navigator.setButtons.mock.calls[1][0]).toEqual({rightButtons: [button]}); + const button = {enabled: false, id: 'add-members', text: 'Add', showAsAction: 'always'}; + expect(baseProps.actions.setButtons).toBeCalledTimes(2); + expect(baseProps.actions.setButtons.mock.calls[0][0]).toEqual(baseProps.componentId, {rightButtons: [button]}); + expect(baseProps.actions.setButtons.mock.calls[1][0]).toEqual(baseProps.componentId, {rightButtons: [button]}); }); test('should match state on clearSearch', () => { @@ -50,12 +48,11 @@ describe('ChannelAddMembers', () => { wrapper.setState({term: '', searchResults: []}); }); - test('should call props.navigator on close', () => { + test('should call props.popTopScreen on close', () => { const wrapper = shallowWithIntl(); wrapper.instance().close(); - expect(baseProps.navigator.pop).toBeCalledTimes(1); - expect(baseProps.navigator.pop).toBeCalledWith({animated: true}); + expect(baseProps.actions.popTopScreen).toBeCalledTimes(1); }); test('should match state on onProfilesLoaded', () => { diff --git a/app/screens/channel_add_members/index.js b/app/screens/channel_add_members/index.js index f44704853..c2efa8625 100644 --- a/app/screens/channel_add_members/index.js +++ b/app/screens/channel_add_members/index.js @@ -11,6 +11,7 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId, getProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users'; +import {setButtons, popTopScreen} from 'app/actions/navigation'; import {handleAddChannelMembers} from 'app/actions/views/channel_add_members'; import ChannelAddMembers from './channel_add_members'; @@ -35,6 +36,8 @@ function mapDispatchToProps(dispatch) { getProfilesNotInChannel, handleAddChannelMembers, searchProfiles, + setButtons, + popTopScreen, }, dispatch), }; } diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index cb2e3b08e..c2a07a787 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -42,6 +42,10 @@ export default class ChannelInfo extends PureComponent { selectPenultimateChannel: PropTypes.func.isRequired, handleSelectChannel: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }), componentId: PropTypes.string, viewArchivedChannels: PropTypes.bool.isRequired, @@ -50,7 +54,6 @@ export default class ChannelInfo extends PureComponent { currentChannelCreatorName: PropTypes.string, currentChannelMemberCount: PropTypes.number, currentUserId: PropTypes.string, - navigator: PropTypes.object, status: PropTypes.string, theme: PropTypes.object.isRequired, isChannelMuted: PropTypes.bool.isRequired, @@ -105,95 +108,61 @@ export default class ChannelInfo extends PureComponent { } close = (redirect = true) => { + const {actions} = this.props; + if (redirect) { - this.props.actions.setChannelDisplayName(''); + actions.setChannelDisplayName(''); } if (Platform.OS === 'android') { - this.props.navigator.dismissModal({animated: true}); + actions.dismissModal(); } else { - this.props.navigator.pop({animated: true}); + actions.popTopScreen(); } }; goToChannelAddMembers = preventDoubleTap(() => { + const {actions} = this.props; const {intl} = this.context; - const {navigator, theme} = this.props; - navigator.push({ - backButtonTitle: '', - screen: 'ChannelAddMembers', - title: intl.formatMessage({id: 'channel_header.addMembers', defaultMessage: 'Add Members'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const screen = 'ChannelAddMembers'; + const title = intl.formatMessage({id: 'channel_header.addMembers', defaultMessage: 'Add Members'}); + + actions.goToScreen(screen, title); }); goToChannelMembers = preventDoubleTap(() => { + const {actions, canManageUsers} = this.props; const {intl} = this.context; - const {canManageUsers, navigator, theme} = this.props; const id = canManageUsers ? t('channel_header.manageMembers') : t('channel_header.viewMembers'); const defaultMessage = canManageUsers ? 'Manage Members' : 'View Members'; + const screen = 'ChannelMembers'; + const title = intl.formatMessage({id, defaultMessage}); - navigator.push({ - backButtonTitle: '', - screen: 'ChannelMembers', - title: intl.formatMessage({id, defaultMessage}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); goToPinnedPosts = preventDoubleTap(() => { + const {actions, currentChannel} = this.props; const {formatMessage} = this.context.intl; - const {actions, currentChannel, navigator, theme} = this.props; const id = t('channel_header.pinnedPosts'); const defaultMessage = 'Pinned Posts'; + const screen = 'PinnedPosts'; + const title = formatMessage({id, defaultMessage}); + const passProps = { + currentChannelId: currentChannel.id, + }; - actions.clearPinnedPosts(currentChannel.id); - navigator.push({ - backButtonTitle: '', - screen: 'PinnedPosts', - title: formatMessage({id, defaultMessage}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentChannelId: currentChannel.id, - }, - }); + actions.goToScreen(screen, title, passProps); }); handleChannelEdit = preventDoubleTap(() => { + const {actions} = this.props; const {intl} = this.context; - const {navigator, theme} = this.props; const id = t('mobile.channel_info.edit'); const defaultMessage = 'Edit Channel'; + const screen = 'EditChannel'; + const title = intl.formatMessage({id, defaultMessage}); - navigator.push({ - backButtonTitle: '', - screen: 'EditChannel', - title: intl.formatMessage({id, defaultMessage}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); handleLeave = () => { @@ -337,30 +306,22 @@ export default class ChannelInfo extends PureComponent { }); showPermalinkView = (postId) => { - const {actions, navigator} = this.props; + const {actions} = this.props; + const screen = 'Permalink'; + const passProps = { + isPermalink: true, + onClose: this.handleClosePermalink, + }; + const options = { + layout: { + backgroundColor: changeOpacity('#000', 0.2), + }, + }; actions.selectFocusedPostId(postId); - if (!this.showingPermalink) { - const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink: true, - onClose: this.handleClosePermalink, - }, - }; - - this.showingPermalink = true; - navigator.showModal(options); - } + this.showingPermalink = true; + actions.showModalOverCurrentContext(screen, passProps, options); }; renderViewOrManageMembersRow = () => { @@ -510,7 +471,6 @@ export default class ChannelInfo extends PureComponent { currentChannel, currentChannelCreatorName, currentChannelMemberCount, - navigator, status, theme, isBot, @@ -545,7 +505,6 @@ export default class ChannelInfo extends PureComponent { displayName={currentChannel.display_name} header={currentChannel.header} memberCount={currentChannelMemberCount} - navigator={navigator} onPermalinkPress={this.handlePermalinkPress} purpose={currentChannel.purpose} status={status} diff --git a/app/screens/channel_info/channel_info_header.js b/app/screens/channel_info/channel_info_header.js index 0f10ef56e..fad941d7a 100644 --- a/app/screens/channel_info/channel_info_header.js +++ b/app/screens/channel_info/channel_info_header.js @@ -23,7 +23,6 @@ export default class ChannelInfoHeader extends React.PureComponent { memberCount: PropTypes.number, displayName: PropTypes.string.isRequired, header: PropTypes.string, - navigator: PropTypes.object.isRequired, onPermalinkPress: PropTypes.func, purpose: PropTypes.string, status: PropTypes.string, @@ -41,7 +40,6 @@ export default class ChannelInfoHeader extends React.PureComponent { displayName, header, memberCount, - navigator, onPermalinkPress, purpose, status, @@ -88,7 +86,6 @@ export default class ChannelInfoHeader extends React.PureComponent { defaultMessage='Purpose' /> { - this.props.navigator.pop({animated: true}); + this.props.actions.popTopScreen(); }; enableRemoveOption = (enabled) => { - if (this.props.canManageUsers) { - this.props.navigator.setButtons({ - rightButtons: [{...this.removeButton, disabled: !enabled}], + const {actions, canManageUsers, componentId} = this.props; + if (canManageUsers) { + actions.setButtons(componentId, { + rightButtons: [{...this.removeButton, enabled}], }); } }; diff --git a/app/screens/channel_members/channel_members.test.js b/app/screens/channel_members/channel_members.test.js index efb4e750e..4ad691310 100644 --- a/app/screens/channel_members/channel_members.test.js +++ b/app/screens/channel_members/channel_members.test.js @@ -10,11 +10,6 @@ import CustomList from 'app/components/custom_list'; import ChannelMembers from './channel_members'; describe('ChannelMembers', () => { - const navigator = { - setOnNavigatorEvent: jest.fn(), - setButtons: jest.fn(), - }; - const baseProps = { theme: Preferences.THEMES.default, currentUserId: 'current-user-id', @@ -24,8 +19,9 @@ describe('ChannelMembers', () => { getProfilesInChannel: jest.fn().mockImplementation(() => Promise.resolve()), handleRemoveChannelMembers: jest.fn(), searchProfiles: jest.fn(), + setButtons: jest.fn(), + popTopScreen: jest.fn(), }, - navigator, componentId: 'component-id', }; diff --git a/app/screens/channel_members/index.js b/app/screens/channel_members/index.js index b37975c3f..5c6b54f50 100644 --- a/app/screens/channel_members/index.js +++ b/app/screens/channel_members/index.js @@ -4,12 +4,14 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {handleRemoveChannelMembers} from 'app/actions/views/channel_members'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentChannel, canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels'; import {makeGetProfilesInChannel} from 'mattermost-redux/selectors/entities/users'; import {getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users'; +import {setButtons, popTopScreen} from 'app/actions/navigation'; +import {handleRemoveChannelMembers} from 'app/actions/views/channel_members'; + import ChannelMembers from './channel_members'; function makeMapStateToProps() { @@ -40,6 +42,8 @@ function mapDispatchToProps(dispatch) { getProfilesInChannel, handleRemoveChannelMembers, searchProfiles, + setButtons, + popTopScreen, }, dispatch), }; } diff --git a/app/screens/channel_peek/channel_peek.js b/app/screens/channel_peek/channel_peek.js index 162cdd85b..f517ba967 100644 --- a/app/screens/channel_peek/channel_peek.js +++ b/app/screens/channel_peek/channel_peek.js @@ -20,7 +20,6 @@ export default class ChannelPeek extends PureComponent { channelId: PropTypes.string.isRequired, currentUserId: PropTypes.string, lastViewedAt: PropTypes.number, - navigator: PropTypes.object, postIds: PropTypes.array, theme: PropTypes.object.isRequired, }; @@ -72,7 +71,6 @@ export default class ChannelPeek extends PureComponent { channelId, currentUserId, lastViewedAt, - navigator, theme, } = this.props; @@ -89,7 +87,6 @@ export default class ChannelPeek extends PureComponent { currentUserId={currentUserId} lastViewedAt={lastViewedAt} channelId={channelId} - navigator={navigator} /> ); diff --git a/app/screens/create_channel/create_channel.js b/app/screens/create_channel/create_channel.js index 18c379e73..1e5994e7c 100644 --- a/app/screens/create_channel/create_channel.js +++ b/app/screens/create_channel/create_channel.js @@ -19,7 +19,6 @@ import {setNavigatorStyles} from 'app/utils/theme'; export default class CreateChannel extends PureComponent { static propTypes = { componentId: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, deviceWidth: PropTypes.number.isRequired, deviceHeight: PropTypes.number.isRequired, @@ -28,6 +27,9 @@ export default class CreateChannel extends PureComponent { closeButton: PropTypes.object, actions: PropTypes.shape({ handleCreateChannel: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, }), }; @@ -45,7 +47,7 @@ export default class CreateChannel extends PureComponent { rightButton = { id: 'create-channel', - disabled: true, + enabled: false, showAsAction: 'always', }; @@ -60,7 +62,7 @@ export default class CreateChannel extends PureComponent { header: '', }; - this.rightButton.title = context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}); + this.rightButton.text = context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}); if (props.closeButton) { this.left = {...this.leftButton, icon: props.closeButton}; @@ -74,7 +76,7 @@ export default class CreateChannel extends PureComponent { buttons.leftButtons = [this.left]; } - props.navigator.setButtons(buttons); + props.actions.setButtons(props.componentId, buttons); } componentDidMount() { @@ -123,37 +125,38 @@ export default class CreateChannel extends PureComponent { } close = (goBack = false) => { + const {actions} = this.props; if (goBack) { - this.props.navigator.pop({animated: true}); + actions.popTopScreen(); } else { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + actions.dismissModal(); } }; emitCanCreateChannel = (enabled) => { + const {actions, componentId} = this.props; const buttons = { - rightButtons: [{...this.rightButton, disabled: !enabled}], + rightButtons: [{...this.rightButton, enabled}], }; if (this.left) { buttons.leftButtons = [this.left]; } - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; emitCreating = (loading) => { + const {actions, componentId} = this.props; const buttons = { - rightButtons: [{...this.rightButton, disabled: loading}], + rightButtons: [{...this.rightButton, enabled: !loading}], }; if (this.left) { buttons.leftButtons = [this.left]; } - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; onCreateChannel = () => { @@ -176,7 +179,6 @@ export default class CreateChannel extends PureComponent { render() { const { - navigator, theme, deviceWidth, deviceHeight, @@ -191,7 +193,6 @@ export default class CreateChannel extends PureComponent { return ( { + const {actions, componentId} = this.props; const buttons = { - rightButtons: [{...this.rightButton, disabled: !enabled}], + rightButtons: [{...this.rightButton, enabled}], }; - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; emitUpdating = (loading) => { + const {actions, componentId} = this.props; const buttons = { - rightButtons: [{...this.rightButton, disabled: loading}], + rightButtons: [{...this.rightButton, enabled: !loading}], }; - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; validateDisplayName = (displayName) => { @@ -277,7 +280,6 @@ export default class EditChannel extends PureComponent { purpose: oldPurpose, type, }, - navigator, theme, currentTeamUrl, deviceWidth, @@ -294,7 +296,6 @@ export default class EditChannel extends PureComponent { return ( { + const {actions, componentId} = this.props; const buttons = { - rightButtons: [{...this.rightButton, disabled: !enabled}], + rightButtons: [{...this.rightButton, enabled}], }; - this.props.actions.setButtons(buttons); + actions.setButtons(componentId, buttons); }; handleRequestError = (error) => { diff --git a/app/screens/more_channels/index.js b/app/screens/more_channels/index.js index 679744979..fa8617a0b 100644 --- a/app/screens/more_channels/index.js +++ b/app/screens/more_channels/index.js @@ -12,11 +12,12 @@ import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/ import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {showCreateOption} from 'mattermost-redux/utils/channel_utils'; import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils'; - -import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; +import {setButtons, dismissModal, goToScreen} from 'app/actions/navigation'; +import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel'; + import MoreChannels from './more_channels'; const joinableChannels = createSelector( @@ -53,6 +54,9 @@ function mapDispatchToProps(dispatch) { getChannels, searchChannels, setChannelDisplayName, + setButtons, + dismissModal, + goToScreen, }, dispatch), }; } diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js index ac118b36f..416a0bf53 100644 --- a/app/screens/more_channels/more_channels.js +++ b/app/screens/more_channels/more_channels.js @@ -29,6 +29,9 @@ export default class MoreChannels extends PureComponent { getChannels: PropTypes.func.isRequired, searchChannels: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, canCreateChannels: PropTypes.bool.isRequired, @@ -36,7 +39,6 @@ export default class MoreChannels extends PureComponent { closeButton: PropTypes.object, currentUserId: PropTypes.string.isRequired, currentTeamId: PropTypes.string.isRequired, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, }; @@ -65,7 +67,7 @@ export default class MoreChannels extends PureComponent { this.rightButton = { id: 'create-pub-channel', - title: context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}), + text: context.intl.formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}), showAsAction: 'always', }; @@ -82,7 +84,7 @@ export default class MoreChannels extends PureComponent { buttons.rightButtons = [this.rightButton]; } - props.navigator.setButtons(buttons); + props.actions.setButtons(props.componentId, buttons); } componentDidMount() { @@ -136,7 +138,7 @@ export default class MoreChannels extends PureComponent { }; close = () => { - this.props.navigator.dismissModal({animationType: 'slide-down'}); + this.props.actions.dismissModal(); }; doGetChannels = () => { @@ -164,16 +166,16 @@ export default class MoreChannels extends PureComponent { getChannels = debounce(this.doGetChannels, 100); headerButtons = (createEnabled) => { - const {canCreateChannels} = this.props; + const {actions, canCreateChannels, componentId} = this.props; const buttons = { leftButtons: [this.leftButton], }; if (canCreateChannels) { - buttons.rightButtons = [{...this.rightButton, disabled: !createEnabled}]; + buttons.rightButtons = [{...this.rightButton, enabled: createEnabled}]; } - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; loadedChannels = ({data}) => { @@ -228,25 +230,16 @@ export default class MoreChannels extends PureComponent { }; onCreateChannel = () => { + const {actions} = this.props; const {formatMessage} = this.context.intl; - const {navigator, theme} = this.props; - navigator.push({ - screen: 'CreateChannel', - animationType: 'slide-up', - title: formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}), - backButtonTitle: '', - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelType: General.OPEN_CHANNEL, - }, - }); + const screen = 'CreateChannel'; + const title = formatMessage({id: 'mobile.create_channel.public', defaultMessage: 'New Public Channel'}); + const passProps = { + channelType: General.OPEN_CHANNEL, + }; + + actions.goToScreen(screen, title, passProps); }; renderLoading = () => { diff --git a/app/screens/more_channels/more_channels.test.js b/app/screens/more_channels/more_channels.test.js index 32eaee752..7fe062b9f 100644 --- a/app/screens/more_channels/more_channels.test.js +++ b/app/screens/more_channels/more_channels.test.js @@ -11,18 +11,15 @@ import MoreChannels from './more_channels.js'; jest.mock('react-intl'); describe('MoreChannels', () => { - const navigator = { - setButtons: jest.fn(), - dismissModal: jest.fn(), - push: jest.fn(), - }; - const actions = { handleSelectChannel: jest.fn(), joinChannel: jest.fn(), getChannels: jest.fn().mockResolvedValue({data: [{id: 'id2', name: 'name2', display_name: 'display_name2'}]}), searchChannels: jest.fn(), setChannelDisplayName: jest.fn(), + setButtons: jest.fn(), + dismissModal: jest.fn(), + goToScreen: jest.fn(), }; const baseProps = { @@ -32,7 +29,6 @@ describe('MoreChannels', () => { closeButton: {}, currentUserId: 'current_user_id', currentTeamId: 'current_team_id', - navigator, theme: Preferences.THEMES.default, componentId: 'component-id', }; @@ -46,27 +42,25 @@ describe('MoreChannels', () => { expect(wrapper.getElement()).toMatchSnapshot(); }); - test('should call props.navigator.dismissModal on close', () => { + test('should call props.actions.dismissModal on close', () => { const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, ); wrapper.instance().close(); - expect(baseProps.navigator.dismissModal).toHaveBeenCalledTimes(1); - expect(baseProps.navigator.dismissModal).toHaveBeenCalledWith({animationType: 'slide-down'}); + expect(baseProps.actions.dismissModal).toHaveBeenCalledTimes(1); }); - test('should call props.navigator.setButtons on headerButtons', () => { - const props = {...baseProps, navigator: {...navigator, setButtons: jest.fn()}}; + test('should call props.actions.setButtons on headerButtons', () => { const wrapper = shallow( - , + , {context: {intl: {formatMessage: jest.fn()}}}, ); - expect(props.navigator.setButtons).toHaveBeenCalledTimes(1); + expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(1); wrapper.instance().headerButtons(true); - expect(props.navigator.setButtons).toHaveBeenCalledTimes(2); + expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(2); }); test('should match return value of filterChannels', () => { From c278614ca13fc8f08c8820956face9f1fc25edb7 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Wed, 26 Jun 2019 15:10:41 -0700 Subject: [PATCH 13/19] [MM-16137] Update post related screens (#2916) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Update CreateChannel and children * Update MoreChannels * Update ChannelPeek and children * Update ChannelNavBar and children * Update ChannelPostList and children * Update ChannelInfo and children * Update ChannelAddMembers * Update ChannelMembers * Update EditChannel * Fix setting of top bar buttons * Update Channel screen and children * Update PinnedPosts * Update LongPost * Update PostOptions * Update PostTextboxBase * Update EditPost * Check-styles fix * Remove navigationComponentId --- .../post_textbox/post_textbox_base.js | 4 +- app/screens/edit_post/edit_post.js | 21 +++--- app/screens/edit_post/index.js | 4 ++ app/screens/long_post/index.js | 3 + app/screens/long_post/long_post.js | 40 ++++------- app/screens/pinned_posts/index.js | 10 ++- app/screens/pinned_posts/pinned_posts.js | 66 +++++++------------ app/screens/post_options/index.js | 3 + app/screens/post_options/post_options.js | 57 ++++++---------- app/screens/post_options/post_options.test.js | 9 +-- 10 files changed, 91 insertions(+), 126 deletions(-) diff --git a/app/components/post_textbox/post_textbox_base.js b/app/components/post_textbox/post_textbox_base.js index d9d884743..fdc5d12bc 100644 --- a/app/components/post_textbox/post_textbox_base.js +++ b/app/components/post_textbox/post_textbox_base.js @@ -62,7 +62,6 @@ export default class PostTextBoxBase extends PureComponent { files: PropTypes.array, maxFileSize: PropTypes.number.isRequired, maxMessageLength: PropTypes.number.isRequired, - navigator: PropTypes.object, rootId: PropTypes.string, theme: PropTypes.object.isRequired, uploadFileRequestStatus: PropTypes.string.isRequired, @@ -182,7 +181,7 @@ export default class PostTextBoxBase extends PureComponent { }; getAttachmentButton = () => { - const {canUploadFiles, channelIsReadOnly, files, maxFileSize, navigator, theme} = this.props; + const {canUploadFiles, channelIsReadOnly, files, maxFileSize, theme} = this.props; let attachmentButton = null; if (canUploadFiles && !channelIsReadOnly) { @@ -190,7 +189,6 @@ export default class PostTextBoxBase extends PureComponent { { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); }; emitCanEditPost = (enabled) => { - this.props.navigator.setButtons({ + const {actions, componentId} = this.props; + actions.setButtons(componentId, { leftButtons: [{...this.leftButton, icon: this.props.closeButton}], - rightButtons: [{...this.rightButton, disabled: !enabled}], + rightButtons: [{...this.rightButton, enabled}], }); }; emitEditing = (loading) => { - this.props.navigator.setButtons({ + const {actions, componentId} = this.props; + actions.setButtons(componentId, { leftButtons: [{...this.leftButton, icon: this.props.closeButton}], - rightButtons: [{...this.rightButton, disabled: loading}], + rightButtons: [{...this.rightButton, enabled: !loading}], }); }; diff --git a/app/screens/edit_post/index.js b/app/screens/edit_post/index.js index 3d0f296d6..435fb3741 100644 --- a/app/screens/edit_post/index.js +++ b/app/screens/edit_post/index.js @@ -7,6 +7,8 @@ import {connect} from 'react-redux'; import {editPost} from 'mattermost-redux/actions/posts'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {setButtons, dismissModal} from 'app/actions/navigation'; + import {getDimensions} from 'app/selectors/device'; import EditPost from './edit_post'; @@ -26,6 +28,8 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ editPost, + setButtons, + dismissModal, }, dispatch), }; } diff --git a/app/screens/long_post/index.js b/app/screens/long_post/index.js index 2040d9a72..3352ccdca 100644 --- a/app/screens/long_post/index.js +++ b/app/screens/long_post/index.js @@ -9,6 +9,7 @@ import {makeGetChannel} from 'mattermost-redux/selectors/entities/channels'; import {getPost} from 'mattermost-redux/selectors/entities/posts'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {dismissModal, goToScreen} from 'app/actions/navigation'; import {loadThreadIfNecessary} from 'app/actions/views/channel'; import LongPost from './long_post'; @@ -35,6 +36,8 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ loadThreadIfNecessary, selectPost, + dismissModal, + goToScreen, }, dispatch), }; } diff --git a/app/screens/long_post/long_post.js b/app/screens/long_post/long_post.js index 554227e67..d05937850 100644 --- a/app/screens/long_post/long_post.js +++ b/app/screens/long_post/long_post.js @@ -44,6 +44,8 @@ export default class LongPost extends PureComponent { actions: PropTypes.shape({ loadThreadIfNecessary: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, channelName: PropTypes.string, fileIds: PropTypes.array, @@ -51,7 +53,6 @@ export default class LongPost extends PureComponent { isPermalink: PropTypes.bool, inThreadView: PropTypes.bool, managedConfig: PropTypes.object, - navigator: PropTypes.object, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, postId: PropTypes.string.isRequired, @@ -77,37 +78,27 @@ export default class LongPost extends PureComponent { } goToThread = preventDoubleTap((post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, + }; actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, - }; - - navigator.push(options); + actions.goToScreen(screen, title, passProps); }); handleClose = () => { - const {navigator} = this.props; + const {actions} = this.props; if (this.refs.view) { this.refs.view.zoomOut().then(() => { - navigator.dismissModal({animationType: 'none'}); + actions.dismissModal(); }); } }; @@ -125,7 +116,6 @@ export default class LongPost extends PureComponent { renderFileAttachments(style) { const { fileIds, - navigator, postId, } = this.props; @@ -139,7 +129,6 @@ export default class LongPost extends PureComponent { onLongPress={emptyFunction} postId={postId} toggleSelected={emptyFunction} - navigator={navigator} /> ); @@ -148,7 +137,7 @@ export default class LongPost extends PureComponent { } renderReactions = (style) => { - const {hasReactions, navigator, postId} = this.props; + const {hasReactions, postId} = this.props; if (!hasReactions) { return null; @@ -157,7 +146,6 @@ export default class LongPost extends PureComponent { return ( @@ -171,7 +159,6 @@ export default class LongPost extends PureComponent { fileIds, hasReactions, managedConfig, - navigator, onHashtagPress, onPermalinkPress, postId, @@ -236,7 +223,6 @@ export default class LongPost extends PureComponent { showLongPost={true} onHashtagPress={onHashtagPress} onPermalinkPress={onPermalinkPress} - navigator={navigator} managedConfig={managedConfig} /> diff --git a/app/screens/pinned_posts/index.js b/app/screens/pinned_posts/index.js index bfc5df20d..30f18d440 100644 --- a/app/screens/pinned_posts/index.js +++ b/app/screens/pinned_posts/index.js @@ -9,8 +9,13 @@ import {clearSearch, getPinnedPosts} from 'mattermost-redux/actions/search'; import {RequestStatus} from 'mattermost-redux/constants'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import { + dismissModal, + goToScreen, + showSearchModal, + showModalOverCurrentContext, +} from 'app/actions/navigation'; import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel'; -import {showSearchModal} from 'app/actions/views/search'; import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list'; import PinnedPosts from './pinned_posts'; @@ -43,7 +48,10 @@ function mapDispatchToProps(dispatch) { getPinnedPosts, selectFocusedPostId, selectPost, + dismissModal, + goToScreen, showSearchModal, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/screens/pinned_posts/pinned_posts.js b/app/screens/pinned_posts/pinned_posts.js index 2d36da00a..b76c18b93 100644 --- a/app/screens/pinned_posts/pinned_posts.js +++ b/app/screens/pinned_posts/pinned_posts.js @@ -35,12 +35,14 @@ export default class PinnedPosts extends PureComponent { getPinnedPosts: PropTypes.func.isRequired, selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, showSearchModal: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, currentChannelId: PropTypes.string.isRequired, didFail: PropTypes.bool, isLoading: PropTypes.bool, - navigator: PropTypes.object, postIds: PropTypes.array, theme: PropTypes.object.isRequired, }; @@ -63,38 +65,24 @@ export default class PinnedPosts extends PureComponent { navigationButtonPressed({buttonId}) { if (buttonId === 'close-settings') { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); } } goToThread = (post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); - + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, + }; Keyboard.dismiss(); actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, - }; - - navigator.push(options); + actions.goToScreen(screen, title, passProps); }; handleClosePermalink = () => { @@ -109,11 +97,11 @@ export default class PinnedPosts extends PureComponent { }; handleHashtagPress = async (hashtag) => { - const {actions, navigator} = this.props; + const {actions} = this.props; - await navigator.dismissModal(); + await actions.dismissModal(); - actions.showSearchModal(navigator, '#' + hashtag); + actions.showSearchModal('#' + hashtag); }; keyExtractor = (item) => item; @@ -165,7 +153,6 @@ export default class PinnedPosts extends PureComponent { previewPost={this.previewPost} highlightPinnedOrFlagged={true} goToThread={this.goToThread} - navigator={this.props.navigator} onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={mattermostManaged.getCachedConfig()} @@ -179,29 +166,24 @@ export default class PinnedPosts extends PureComponent { }; showPermalinkView = (postId, isPermalink) => { - const {actions, navigator} = this.props; + const {actions} = this.props; actions.selectFocusedPostId(postId); if (!this.showingPermalink) { + const screen = 'Permalink'; + const passProps = { + isPermalink, + onClose: this.handleClosePermalink, + }; const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink, - onClose: this.handleClosePermalink, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; this.showingPermalink = true; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); } }; diff --git a/app/screens/post_options/index.js b/app/screens/post_options/index.js index aa0fd3b7f..e6120ef8e 100644 --- a/app/screens/post_options/index.js +++ b/app/screens/post_options/index.js @@ -22,6 +22,7 @@ import {getCurrentTeamId, getCurrentTeamUrl} from 'mattermost-redux/selectors/en import {canEditPost} from 'mattermost-redux/utils/post_utils'; import {THREAD} from 'app/constants/screen'; +import {dismissModal, showModal} from 'app/actions/navigation'; import {addReaction} from 'app/actions/views/emoji'; import {getDimensions} from 'app/selectors/device'; @@ -121,6 +122,8 @@ function mapDispatchToProps(dispatch) { removePost, unflagPost, unpinPost, + dismissModal, + showModal, }, dispatch), }; } diff --git a/app/screens/post_options/post_options.js b/app/screens/post_options/post_options.js index 8f2be0ad7..64c934ebd 100644 --- a/app/screens/post_options/post_options.js +++ b/app/screens/post_options/post_options.js @@ -25,6 +25,8 @@ export default class PostOptions extends PureComponent { removePost: PropTypes.func.isRequired, unflagPost: PropTypes.func.isRequired, unpinPost: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + showModal: PropTypes.func.isRequired, }).isRequired, canAddReaction: PropTypes.bool, canReply: PropTypes.bool, @@ -39,7 +41,6 @@ export default class PostOptions extends PureComponent { deviceHeight: PropTypes.number.isRequired, isFlagged: PropTypes.bool, isMyPost: PropTypes.bool, - navigator: PropTypes.object.isRequired, post: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, }; @@ -49,9 +50,7 @@ export default class PostOptions extends PureComponent { }; close = () => { - this.props.navigator.dismissModal({ - animationType: 'none', - }); + this.props.actions.dismissModal(); }; closeWithAnimation = () => { @@ -266,27 +265,20 @@ export default class PostOptions extends PureComponent { }; handleAddReaction = () => { + const {actions, theme} = this.props; const {formatMessage} = this.context.intl; - const {navigator, theme} = this.props; this.close(); setTimeout(() => { MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => { - navigator.showModal({ - screen: 'AddReaction', - title: formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - closeButton: source, - onEmojiPress: this.handleAddReactionToPost, - }, - }); + const screen = 'AddReaction'; + const title = formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}); + const passProps = { + closeButton: source, + onEmojiPress: this.handleAddReactionToPost, + }; + + actions.showModal(screen, title, passProps); }); }, 300); }; @@ -364,27 +356,20 @@ export default class PostOptions extends PureComponent { }; handlePostEdit = () => { + const {actions, theme, post} = this.props; const {intl} = this.context; - const {navigator, post, theme} = this.props; this.close(); setTimeout(() => { MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => { - navigator.showModal({ - screen: 'EditPost', - title: intl.formatMessage({id: 'mobile.edit_post.title', defaultMessage: 'Editing Message'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - post, - closeButton: source, - }, - }); + const screen = 'EditPost'; + const title = intl.formatMessage({id: 'mobile.edit_post.title', defaultMessage: 'Editing Message'}); + const passProps = { + post, + closeButton: source, + }; + + actions.showModal(screen, title, passProps); }); }, 300); }; diff --git a/app/screens/post_options/post_options.test.js b/app/screens/post_options/post_options.test.js index dc0498c73..cd742c97f 100644 --- a/app/screens/post_options/post_options.test.js +++ b/app/screens/post_options/post_options.test.js @@ -18,12 +18,6 @@ jest.mock('Alert', () => { }); describe('PostOptions', () => { - const navigator = { - showModal: jest.fn(), - dismissModal: jest.fn(), - push: jest.fn(), - }; - const actions = { addReaction: jest.fn(), deletePost: jest.fn(), @@ -32,6 +26,8 @@ describe('PostOptions', () => { removePost: jest.fn(), unflagPost: jest.fn(), unpinPost: jest.fn(), + dismissModal: jest.fn(), + showModal: jest.fn(), }; const post = { @@ -58,7 +54,6 @@ describe('PostOptions', () => { isMyPost: true, isSystemMessage: false, managedConfig: {}, - navigator, post, showAddReaction: true, theme: Preferences.THEMES.default, From 30375aa4a2148fdde29a5df4e90015b39d68b66e Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Fri, 28 Jun 2019 09:03:18 -0700 Subject: [PATCH 14/19] [MM-16138] Update settings related screens (#2918) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Update CreateChannel and children * Update MoreChannels * Update ChannelPeek and children * Update ChannelNavBar and children * Update ChannelPostList and children * Update ChannelInfo and children * Update ChannelAddMembers * Update ChannelMembers * Update EditChannel * Fix setting of top bar buttons * Update Channel screen and children * Update PinnedPosts * Update LongPost * Update PostOptions * Update PostTextboxBase * Update EditPost * Check-styles fix * Update DisplaySettings * Update Timezone * Update SelectTimezone * Remove navigationComponentId --- .../display_settings/display_settings.js | 55 +++++-------------- .../display_settings/display_settings.test.js | 6 +- .../settings/display_settings/index.js | 14 ++++- app/screens/timezone/index.js | 2 + app/screens/timezone/select_timezone/index.js | 13 ++++- .../select_timezone/select_timezone.js | 6 +- app/screens/timezone/timezone.js | 28 +++------- 7 files changed, 57 insertions(+), 67 deletions(-) diff --git a/app/screens/settings/display_settings/display_settings.js b/app/screens/settings/display_settings/display_settings.js index 19d135d35..a4ca423c5 100644 --- a/app/screens/settings/display_settings/display_settings.js +++ b/app/screens/settings/display_settings/display_settings.js @@ -18,8 +18,10 @@ import ClockDisplay from 'app/screens/clock_display'; export default class DisplaySettings extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, enableTheme: PropTypes.bool.isRequired, enableTimezone: PropTypes.bool.isRequired, @@ -38,22 +40,13 @@ export default class DisplaySettings extends PureComponent { }; goToClockDisplaySettings = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; if (Platform.OS === 'ios') { - navigator.push({ - screen: 'ClockDisplay', - title: intl.formatMessage({id: 'user.settings.display.clockDisplay', defaultMessage: 'Clock Display'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + const screen = 'ClockDisplay'; + const title = intl.formatMessage({id: 'user.settings.display.clockDisplay', defaultMessage: 'Clock Display'}); + actions.goToScreen(screen, title); return; } @@ -61,39 +54,21 @@ export default class DisplaySettings extends PureComponent { }); goToTimezoneSettings = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; + const screen = 'TimezoneSettings'; + const title = intl.formatMessage({id: 'mobile.advanced_settings.timezone', defaultMessage: 'Timezone'}); - navigator.push({ - screen: 'TimezoneSettings', - title: intl.formatMessage({id: 'mobile.advanced_settings.timezone', defaultMessage: 'Timezone'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); goToThemeSettings = preventDoubleTap(() => { - const {navigator, theme} = this.props; + const {actions} = this.props; const {intl} = this.context; + const screen = 'ThemeSettings'; + const title = intl.formatMessage({id: 'mobile.display_settings.theme', defaultMessage: 'Theme'}); - navigator.push({ - screen: 'ThemeSettings', - title: intl.formatMessage({id: 'mobile.display_settings.theme', defaultMessage: 'Theme'}), - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + actions.goToScreen(screen, title); }); render() { diff --git a/app/screens/settings/display_settings/display_settings.test.js b/app/screens/settings/display_settings/display_settings.test.js index bd401a916..54ae73fd7 100644 --- a/app/screens/settings/display_settings/display_settings.test.js +++ b/app/screens/settings/display_settings/display_settings.test.js @@ -12,12 +12,12 @@ jest.mock('react-intl'); describe('DisplaySettings', () => { const baseProps = { + actions: { + goToScreen: jest.fn(), + }, theme: Preferences.THEMES.default, enableTheme: false, enableTimezone: false, - navigator: { - push: jest.fn(), - }, componentId: 'component-id', }; diff --git a/app/screens/settings/display_settings/index.js b/app/screens/settings/display_settings/index.js index 9c9a6c80c..12d4b321f 100644 --- a/app/screens/settings/display_settings/index.js +++ b/app/screens/settings/display_settings/index.js @@ -1,15 +1,17 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; +import {goToScreen} from 'app/actions/navigation'; +import {getAllowedThemes} from 'app/selectors/theme'; import {isThemeSwitchingEnabled} from 'app/utils/theme'; import DisplaySettings from './display_settings'; -import {getAllowedThemes} from 'app/selectors/theme'; function mapStateToProps(state) { const enableTimezone = isTimezoneEnabled(state); @@ -22,4 +24,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(DisplaySettings); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(DisplaySettings); diff --git a/app/screens/timezone/index.js b/app/screens/timezone/index.js index fb2901c89..8b015bc5a 100644 --- a/app/screens/timezone/index.js +++ b/app/screens/timezone/index.js @@ -10,6 +10,7 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; +import {goToScreen} from 'app/actions/navigation'; import {updateUser} from 'app/actions/views/edit_profile'; import Timezone from './timezone'; @@ -32,6 +33,7 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ getSupportedTimezones, updateUser, + goToScreen, }, dispatch), }; } diff --git a/app/screens/timezone/select_timezone/index.js b/app/screens/timezone/select_timezone/index.js index 5fb95760f..4def5ad3e 100644 --- a/app/screens/timezone/select_timezone/index.js +++ b/app/screens/timezone/select_timezone/index.js @@ -1,11 +1,14 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getSupportedTimezones} from 'mattermost-redux/selectors/entities/general'; +import {popTopScreen} from 'app/actions/navigation'; + import SelectTimezone from './select_timezone'; function mapStateToProps(state, props) { @@ -26,4 +29,12 @@ function mapStateToProps(state, props) { }; } -export default connect(mapStateToProps)(SelectTimezone); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + popTopScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(SelectTimezone); diff --git a/app/screens/timezone/select_timezone/select_timezone.js b/app/screens/timezone/select_timezone/select_timezone.js index f52559010..79f3a20ce 100644 --- a/app/screens/timezone/select_timezone/select_timezone.js +++ b/app/screens/timezone/select_timezone/select_timezone.js @@ -23,10 +23,12 @@ const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS; export default class Timezone extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + popTopScreen: PropTypes.func.isRequired, + }).isRequired, selectedTimezone: PropTypes.string.isRequired, initialScrollIndex: PropTypes.number.isRequired, timezones: PropTypes.array.isRequired, - navigator: PropTypes.object, onBack: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, }; @@ -59,7 +61,7 @@ export default class Timezone extends PureComponent { timezoneSelected = (timezone) => { this.props.onBack(timezone); - this.props.navigator.pop(); + this.props.actions.popTopScreen(); }; handleTextChanged = (value) => { diff --git a/app/screens/timezone/timezone.js b/app/screens/timezone/timezone.js index 52ff47116..1badd938e 100644 --- a/app/screens/timezone/timezone.js +++ b/app/screens/timezone/timezone.js @@ -21,7 +21,6 @@ import {getDeviceTimezone} from 'app/utils/timezone'; export default class Timezone extends PureComponent { static propTypes = { - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, timezones: PropTypes.array.isRequired, user: PropTypes.object.isRequired, @@ -116,29 +115,20 @@ export default class Timezone extends PureComponent { goToSelectTimezone = () => { const { + actions, userTimezone: {manualTimezone}, - navigator, - theme, } = this.props; const {intl} = this.context; + const screen = 'SelectTimezone'; + const title = intl.formatMessage({id: 'mobile.timezone_settings.select', defaultMessage: 'Select Timezone'}); + const passProps = { + selectedTimezone: manualTimezone, + onBack: this.updateManualTimezone, + }; + this.goingBack = false; - navigator.push({ - backButtonTitle: '', - screen: 'SelectTimezone', - title: intl.formatMessage({id: 'mobile.timezone_settings.select', defaultMessage: 'Select Timezone'}), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - selectedTimezone: manualTimezone, - onBack: this.updateManualTimezone, - }, - }); + actions.goToScreen(screen, title, passProps); }; render() { From 8762bf05f92064fbec5fdf997d7fa86dc54b4ef1 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 2 Jul 2019 18:40:39 -0700 Subject: [PATCH 15/19] Launch app without authentication if already started --- app/mattermost.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mattermost.js b/app/mattermost.js index 8f2d56903..f9d160281 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -26,7 +26,7 @@ export const store = configureStore(initialState); const init = async () => { if (EphemeralStore.appStarted) { - launchAppAndAuthenticateIfNeeded(); + launchApp(); return; } From 5eee2b7652ece2ff408997ca02eebe37269508f3 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Mon, 8 Jul 2019 10:03:31 -0700 Subject: [PATCH 16/19] [MM-16139] [MM-16140] [MM-16141] Update the remaining screens/components (#2920) * Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Update CreateChannel and children * Update MoreChannels * Update ChannelPeek and children * Update ChannelNavBar and children * Update ChannelPostList and children * Update ChannelInfo and children * Update ChannelAddMembers * Update ChannelMembers * Update EditChannel * Fix setting of top bar buttons * Update Channel screen and children * Update PinnedPosts * Update LongPost * Update PostOptions * Update PostTextboxBase * Update EditPost * Check-styles fix * Update DisplaySettings * Update Timezone * Update SelectTimezone * Update IntlWrapper and Notification * Update InteractiveDialog and children * Update ExpandedAnnouncementBanner * Update ClientUpgradeListener * Update MoreDirectMessages * Update SelectorScreen * Update UserProfile * Update Thread * Update About * Update ImagePreview * Update TextPreview * Update AddReaction * Update ReactionList and children * Update Code * Update TermsOfService * Update Permalink * Update Permalink * Update ErrorTeamsList * Update Search * Remove navigator prop * Fix setButtons calls * Remove navigationComponentId * Fix test * Handle in-app notifs in global event handler * Fix in-app notification handling * Fix typo * Fix tests * Auto dismiss in-app notif after 5 seconds * Fix typo * Update overlay dismissal * Animate in-app notif dismissal and allow gesture dismissal --- app/actions/navigation.js | 42 ++- app/actions/views/search.js | 26 -- .../profile_picture_button.test.js.snap | 7 - .../client_upgrade_listener.js | 30 +- .../client_upgrade_listener/index.js | 5 +- app/components/message_attachments/index.js | 3 - .../post_attachment_opengraph.test.js | 1 - .../__snapshots__/post_textbox.test.js.snap | 15 +- .../post_textbox/post_textbox.test.js | 3 - app/components/profile_picture_button.test.js | 7 - app/components/root/index.js | 2 - app/components/root/root.js | 37 +-- .../channel_item/channel_item.test.js | 1 - .../sidebars/settings/settings_sidebar.js | 2 + app/constants/navigation.js | 1 + app/constants/view.js | 1 - app/init/global_event_handler.js | 34 ++- app/screens/about/about.js | 1 - app/screens/add_reaction/add_reaction.js | 11 +- app/screens/add_reaction/index.js | 14 +- app/screens/channel/channel.android.js | 11 +- app/screens/channel/channel.ios.js | 8 +- app/screens/code/code.js | 6 +- app/screens/code/index.js | 13 +- .../error_teams_list/error_teams_list.js | 21 +- .../error_teams_list/error_teams_list.test.js | 2 + app/screens/error_teams_list/index.js | 3 + .../expanded_announcement_banner.js | 5 +- .../expanded_announcement_banner/index.js | 2 + app/screens/image_preview/image_preview.js | 56 ++-- .../image_preview/image_preview.test.js | 29 +- app/screens/image_preview/index.js | 16 +- app/screens/index.js | 15 +- .../interactive_dialog/dialog_element.js | 3 - app/screens/interactive_dialog/index.js | 3 + .../interactive_dialog/interactive_dialog.js | 9 +- .../interactive_dialog.test.js | 18 +- app/screens/more_dms/index.js | 4 + app/screens/more_dms/more_dms.js | 12 +- app/screens/notification/index.js | 5 + app/screens/notification/notification.js | 177 +++++++++--- app/screens/permalink/index.js | 12 +- app/screens/permalink/permalink.js | 59 ++-- app/screens/permalink/permalink.test.js | 12 +- .../__snapshots__/reaction_list.test.js.snap | 8 +- app/screens/reaction_list/index.js | 3 + app/screens/reaction_list/reaction_list.js | 8 +- .../reaction_list/reaction_list.test.js | 1 + .../__snapshots__/reaction_row.test.js.snap | 0 .../reaction_list/reaction_row/index.js | 19 ++ .../{ => reaction_row}/reaction_row.js | 27 +- .../{ => reaction_row}/reaction_row.test.js | 4 +- app/screens/search/index.js | 4 +- app/screens/search/search.js | 50 ++-- app/screens/selector_screen/index.js | 3 + .../selector_screen/selector_screen.js | 4 +- .../selector_screen/selector_screen.test.js | 1 + .../terms_of_service.test.js.snap | 266 ------------------ app/screens/terms_of_service/index.js | 9 + .../terms_of_service/terms_of_service.js | 28 +- .../terms_of_service/terms_of_service.test.js | 18 +- app/screens/text_preview/text_preview.js | 1 - .../__snapshots__/thread.ios.test.js.snap | 88 ------ app/screens/thread/index.js | 4 + app/screens/thread/thread.android.js | 3 - app/screens/thread/thread.ios.js | 3 - app/screens/thread/thread.ios.test.js | 40 +-- app/screens/thread/thread_base.js | 47 +--- app/screens/user_profile/index.js | 11 + app/screens/user_profile/user_profile.js | 58 ++-- app/screens/user_profile/user_profile.test.js | 32 +-- app/utils/push_notifications.js | 9 +- app/utils/wrap_context_provider.js | 2 - ios/Podfile.lock | 2 +- 74 files changed, 598 insertions(+), 899 deletions(-) rename app/screens/reaction_list/{ => reaction_row}/__snapshots__/reaction_row.test.js.snap (100%) create mode 100644 app/screens/reaction_list/reaction_row/index.js rename app/screens/reaction_list/{ => reaction_row}/reaction_row.js (82%) rename app/screens/reaction_list/{ => reaction_row}/reaction_row.test.js (91%) diff --git a/app/actions/navigation.js b/app/actions/navigation.js index ca261c905..f1e6852bb 100644 --- a/app/actions/navigation.js +++ b/app/actions/navigation.js @@ -178,7 +178,12 @@ export function popToRoot() { return () => { const componentId = EphemeralStore.getTopComponentId(); - Navigation.popToRoot(componentId); + Navigation.popToRoot(componentId).catch(() => { + // RNN returns a promise rejection if there are no screens + // atop the root screen to pop. We'll do nothing in this + // case but we will catch the rejection here so that the + // caller doesn't have to. + }); }; } @@ -289,7 +294,11 @@ export function dismissModal(options = {}) { export function dismissAllModals(options = {}) { return () => { - Navigation.dismissAllModals(options); + Navigation.dismissAllModals(options).catch(() => { + // RNN returns a promise rejection if there are no modals to + // dismiss. We'll do nothing in this case but we will catch + // the rejection here so that the caller doesn't have to. + }); }; } @@ -321,3 +330,32 @@ export function setButtons(componentId, buttons = {leftButtons: [], rightButtons }); }; } + +export function showOverlay(name, passProps, options = {}) { + return () => { + const defaultOptions = { + overlay: { + interceptTouchOutside: false, + }, + }; + + Navigation.showOverlay({ + component: { + name, + passProps, + options: merge(defaultOptions, options), + }, + }); + }; +} + +export function dismissOverlay(componentId) { + return () => { + return Navigation.dismissOverlay(componentId).catch(() => { + // RNN returns a promise rejection if there is no modal with + // this componentId to dismiss. We'll do nothing in this case + // but we will catch the rejection here so that the caller + // doesn't have to. + }); + }; +} diff --git a/app/actions/views/search.js b/app/actions/views/search.js index 0e6f0c3a2..2ccd0060d 100644 --- a/app/actions/views/search.js +++ b/app/actions/views/search.js @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; - import {ViewTypes} from 'app/constants'; export function handleSearchDraftChanged(text) { @@ -13,27 +11,3 @@ export function handleSearchDraftChanged(text) { }, getState); }; } - -// TODO: Remove this once all screens/components call -// app/actions/navigation's showSearchModal -export function showSearchModal(navigator, initialValue = '') { - return (dispatch, getState) => { - const theme = getTheme(getState()); - - const options = { - screen: 'Search', - animated: true, - backButtonTitle: '', - overrideBackPress: true, - passProps: { - initialValue, - }, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: theme.centerChannelBg, - }, - }; - - navigator.showModal(options); - }; -} diff --git a/app/components/__snapshots__/profile_picture_button.test.js.snap b/app/components/__snapshots__/profile_picture_button.test.js.snap index 97f0ea52d..c4205e2cf 100644 --- a/app/components/__snapshots__/profile_picture_button.test.js.snap +++ b/app/components/__snapshots__/profile_picture_button.test.js.snap @@ -9,13 +9,6 @@ exports[`profile_picture_button should match snapshot 1`] = ` ] } maxFileSize={20971520} - navigator={ - Object { - "dismissModal": [MockFunction], - "push": [MockFunction], - "setButtons": [MockFunction], - } - } theme={ Object { "awayIndicator": "#ffbc42", diff --git a/app/components/client_upgrade_listener/client_upgrade_listener.js b/app/components/client_upgrade_listener/client_upgrade_listener.js index 31631d8ca..b05b3f14f 100644 --- a/app/components/client_upgrade_listener/client_upgrade_listener.js +++ b/app/components/client_upgrade_listener/client_upgrade_listener.js @@ -27,6 +27,8 @@ export default class ClientUpgradeListener extends PureComponent { actions: PropTypes.shape({ logError: PropTypes.func.isRequired, setLastUpgradeCheck: PropTypes.func.isRequired, + showModal: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, currentVersion: PropTypes.string, downloadLink: PropTypes.string, @@ -35,7 +37,6 @@ export default class ClientUpgradeListener extends PureComponent { lastUpgradeCheck: PropTypes.number, latestVersion: PropTypes.string, minVersion: PropTypes.string, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, }; @@ -140,27 +141,26 @@ export default class ClientUpgradeListener extends PureComponent { }; handleLearnMore = () => { + const {actions} = this.props; const {intl} = this.context; - this.props.navigator.dismissModal({animationType: 'none'}); - this.props.navigator.showModal({ - screen: 'ClientUpgrade', - title: intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'}), - navigatorStyle: { - navBarHidden: false, - statusBarHidden: false, - statusBarHideWithNavBar: false, - }, - navigatorButtons: { + actions.dismissModal(); + + const screen = 'ClientUpgrade'; + const title = intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Upgrade App'}); + const passProps = { + upgradeType: this.state.upgradeType, + }; + const options = { + topBar: { leftButtons: [{ id: 'close-upgrade', icon: this.closeButton, }], }, - passProps: { - upgradeType: this.state.upgradeType, - }, - }); + }; + + actions.showModal(screen, title, passProps, options); this.toggleUpgradeMessage(false); }; diff --git a/app/components/client_upgrade_listener/index.js b/app/components/client_upgrade_listener/index.js index 2e66b2377..173c14a50 100644 --- a/app/components/client_upgrade_listener/index.js +++ b/app/components/client_upgrade_listener/index.js @@ -4,11 +4,12 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import {logError} from 'mattermost-redux/actions/errors'; +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {showModal, dismissModal} from 'app/actions/navigation'; import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade'; import getClientUpgrade from 'app/selectors/client_upgrade'; import {isLandscape} from 'app/selectors/device'; -import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import ClientUpgradeListener from './client_upgrade_listener'; @@ -32,6 +33,8 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ logError, setLastUpgradeCheck, + showModal, + dismissModal, }, dispatch), }; } diff --git a/app/components/message_attachments/index.js b/app/components/message_attachments/index.js index e3b287545..7f3c126ce 100644 --- a/app/components/message_attachments/index.js +++ b/app/components/message_attachments/index.js @@ -18,7 +18,6 @@ export default class MessageAttachments extends PureComponent { deviceWidth: PropTypes.number.isRequired, postId: PropTypes.string.isRequired, metadata: PropTypes.object, - navigator: PropTypes.object.isRequired, onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, theme: PropTypes.object, @@ -33,7 +32,6 @@ export default class MessageAttachments extends PureComponent { deviceHeight, deviceWidth, metadata, - navigator, onHashtagPress, onPermalinkPress, postId, @@ -52,7 +50,6 @@ export default class MessageAttachments extends PureComponent { deviceWidth={deviceWidth} key={'att_' + i} metadata={metadata} - navigator={navigator} onHashtagPress={onHashtagPress} onPermalinkPress={onPermalinkPress} postId={postId} diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js index 4d03ae92d..dc1b926ff 100644 --- a/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js +++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.test.js @@ -37,7 +37,6 @@ describe('PostAttachmentOpenGraph', () => { }, isReplyPost: false, link: 'https://mattermost.com/', - navigator: {}, theme: Preferences.THEMES.default, }; diff --git a/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap b/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap index ac7535189..ea489c424 100644 --- a/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap +++ b/app/components/post_textbox/__snapshots__/post_textbox.test.js.snap @@ -16,23 +16,11 @@ exports[`PostTextBox should match, full snapshot 1`] = ` } } > - { files: [], maxFileSize: 1024, maxMessageLength: 4000, - navigator: { - showModal: jest.fn(), - }, rootId: '', theme: Preferences.THEMES.default, uploadFileRequestStatus: 'NOT_STARTED', diff --git a/app/components/profile_picture_button.test.js b/app/components/profile_picture_button.test.js index a46a1a09c..44237c1a0 100644 --- a/app/components/profile_picture_button.test.js +++ b/app/components/profile_picture_button.test.js @@ -10,15 +10,8 @@ import ProfilePictureButton from './profile_picture_button.js'; import {Client4} from 'mattermost-redux/client'; describe('profile_picture_button', () => { - const navigator = { - setButtons: jest.fn(), - dismissModal: jest.fn(), - push: jest.fn(), - }; - const baseProps = { theme: Preferences.THEMES.default, - navigator, currentUser: { first_name: 'Dwight', last_name: 'Schrute', diff --git a/app/components/root/index.js b/app/components/root/index.js index 747bfdb9c..7f4b28031 100644 --- a/app/components/root/index.js +++ b/app/components/root/index.js @@ -4,7 +4,6 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentUrl} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; @@ -19,7 +18,6 @@ function mapStateToProps(state) { return { theme: getTheme(state), - currentChannelId: getCurrentChannelId(state), currentUrl: removeProtocol(getCurrentUrl(state)), locale, }; diff --git a/app/components/root/root.js b/app/components/root/root.js index 051c4c715..95e46af44 100644 --- a/app/components/root/root.js +++ b/app/components/root/root.js @@ -9,7 +9,7 @@ import {Platform} from 'react-native'; import {Client4} from 'mattermost-redux/client'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; -import {NavigationTypes, ViewTypes} from 'app/constants'; +import {NavigationTypes} from 'app/constants'; import {getTranslations} from 'app/i18n'; export default class Root extends PureComponent { @@ -18,9 +18,7 @@ export default class Root extends PureComponent { resetToTeams: PropTypes.func.isRequired, }).isRequired, children: PropTypes.node, - navigator: PropTypes.object, excludeEvents: PropTypes.bool, - currentChannelId: PropTypes.string, currentUrl: PropTypes.string, locale: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, @@ -30,8 +28,6 @@ export default class Root extends PureComponent { Client4.setAcceptLanguage(this.props.locale); if (!this.props.excludeEvents) { - 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); } @@ -45,30 +41,11 @@ export default class Root extends PureComponent { componentWillUnmount() { if (!this.props.excludeEvents) { - 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); } } - handleInAppNotification = (notification) => { - const {data} = notification; - const {currentChannelId, navigator} = this.props; - - if (data && data.channel_id !== currentChannelId) { - navigator.showInAppNotification({ - screen: 'Notification', - position: 'top', - autoDismissTimerSec: 5, - dismissWithSwipe: true, - passProps: { - notification, - }, - }); - } - }; - handleNoTeams = () => { if (!this.refs.provider) { setTimeout(this.handleNoTeams, 200); @@ -119,18 +96,6 @@ export default class Root extends PureComponent { actions.resetToTeams(screen, title, passProps, options); } - handleNotificationTapped = async () => { - const {navigator} = this.props; - - if (Platform.OS === 'android') { - navigator.dismissModal({animation: 'none'}); - } - - navigator.popToRoot({ - animated: false, - }); - }; - render() { const locale = this.props.locale; diff --git a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js index 38334340d..b9ced6211 100644 --- a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js +++ b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js @@ -31,7 +31,6 @@ describe('ChannelItem', () => { isUnread: true, hasDraft: false, mentions: 0, - navigator: {push: () => {}}, // eslint-disable-line no-empty-function onSelectChannel: () => {}, // eslint-disable-line no-empty-function shouldHideChannel: false, showUnreadForMsgs: true, diff --git a/app/components/sidebars/settings/settings_sidebar.js b/app/components/sidebars/settings/settings_sidebar.js index 9a3fff5b3..41a76f970 100644 --- a/app/components/sidebars/settings/settings_sidebar.js +++ b/app/components/sidebars/settings/settings_sidebar.js @@ -68,10 +68,12 @@ export default class SettingsDrawer extends PureComponent { } componentDidMount() { + EventEmitter.on('close_settings_sidebar', this.closeSettingsSidebar); BackHandler.addEventListener('hardwareBackPress', this.handleAndroidBack); } componentWillUnmount() { + EventEmitter.off('close_settings_sidebar', this.closeSettingsSidebar); BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack); } diff --git a/app/constants/navigation.js b/app/constants/navigation.js index 809997da3..618dc5b3f 100644 --- a/app/constants/navigation.js +++ b/app/constants/navigation.js @@ -9,6 +9,7 @@ const NavigationTypes = keyMirror({ NAVIGATION_NO_TEAMS: null, RESTART_APP: null, NAVIGATION_ERROR_TEAMS: null, + NAVIGATION_SHOW_OVERLAY: null, }); export default NavigationTypes; diff --git a/app/constants/view.js b/app/constants/view.js index 0c3dc6b37..010944a69 100644 --- a/app/constants/view.js +++ b/app/constants/view.js @@ -42,7 +42,6 @@ const ViewTypes = keyMirror({ COMMENT_DRAFT_SELECTION_CHANGED: null, NOTIFICATION_IN_APP: null, - NOTIFICATION_TAPPED: null, SET_POST_DRAFT: null, SET_COMMENT_DRAFT: null, diff --git a/app/init/global_event_handler.js b/app/init/global_event_handler.js index df17fb796..4eb258b93 100644 --- a/app/init/global_event_handler.js +++ b/app/init/global_event_handler.js @@ -11,11 +11,13 @@ import {close as closeWebSocket} from 'mattermost-redux/actions/websocket'; import {Client4} from 'mattermost-redux/client'; import {General} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; +import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from 'app/actions/device'; import {selectDefaultChannel} from 'app/actions/views/channel'; +import {showOverlay} from 'app/actions/navigation'; import {loadConfigAndLicense, setDeepLinkURL, startDataCleanup} from 'app/actions/views/root'; -import {NavigationTypes} from 'app/constants'; +import {NavigationTypes, ViewTypes} from 'app/constants'; import {getTranslations} from 'app/i18n'; import mattermostManaged from 'app/mattermost_managed'; import PushNotifications from 'app/push_notifications'; @@ -38,12 +40,15 @@ class GlobalEventHandler { EventEmitter.on(General.SERVER_VERSION_CHANGED, this.onServerVersionChanged); EventEmitter.on(General.CONFIG_CHANGED, this.onServerConfigChanged); EventEmitter.on(General.SWITCH_TO_DEFAULT_CHANNEL, this.onSwitchToDefaultChannel); + this.turnOnInAppNotificationHandling(); Dimensions.addEventListener('change', this.onOrientationChange); AppState.addEventListener('change', this.onAppStateChange); Linking.addEventListener('url', this.onDeepLink); } appActive = async () => { + this.turnOnInAppNotificationHandling(); + // if the app is being controlled by an EMM provider if (emmProvider.enabled && emmProvider.inAppPinCode) { const authExpired = (Date.now() - emmProvider.inBackgroundSince) >= PROMPT_IN_APP_PIN_CODE_AFTER; @@ -57,6 +62,8 @@ class GlobalEventHandler { }; appInactive = () => { + this.turnOffInAppNotificationHandling(); + const {dispatch} = this.store; // When the app is sent to the background we set the time when that happens @@ -227,6 +234,31 @@ class GlobalEventHandler { dispatch(logout()); } }; + + turnOnInAppNotificationHandling = () => { + EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification); + } + + turnOffInAppNotificationHandling = () => { + EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification); + } + + handleInAppNotification = (notification) => { + const {data} = notification; + const {dispatch, getState} = this.store; + const state = getState(); + const currentChannelId = getCurrentChannelId(state); + + if (data && data.channel_id !== currentChannelId) { + const screen = 'Notification'; + const passProps = { + notification, + }; + + EventEmitter.emit(NavigationTypes.NAVIGATION_SHOW_OVERLAY); + dispatch(showOverlay(screen, passProps)); + } + }; } export default new GlobalEventHandler(); diff --git a/app/screens/about/about.js b/app/screens/about/about.js index dc056e9ec..2417768c1 100644 --- a/app/screens/about/about.js +++ b/app/screens/about/about.js @@ -26,7 +26,6 @@ export default class About extends PureComponent { componentId: PropTypes.string, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, }; diff --git a/app/screens/add_reaction/add_reaction.js b/app/screens/add_reaction/add_reaction.js index f24978bf5..e03ddbf85 100644 --- a/app/screens/add_reaction/add_reaction.js +++ b/app/screens/add_reaction/add_reaction.js @@ -15,9 +15,12 @@ import {setNavigatorStyles} from 'app/utils/theme'; export default class AddReaction extends PureComponent { static propTypes = { + actions: PropTypes.shape({ + dismissModal: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string, closeButton: PropTypes.object, - navigator: PropTypes.object.isRequired, onEmojiPress: PropTypes.func, theme: PropTypes.object.isRequired, }; @@ -33,7 +36,7 @@ export default class AddReaction extends PureComponent { constructor(props) { super(props); - props.navigator.setButtons({ + props.actions.setButtons(props.componentId, { leftButtons: [{...this.leftButton, icon: props.closeButton}], }); } @@ -55,9 +58,7 @@ export default class AddReaction extends PureComponent { } close = () => { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); }; handleEmojiPress = (emoji) => { diff --git a/app/screens/add_reaction/index.js b/app/screens/add_reaction/index.js index 1c3d7556b..eb24346b2 100644 --- a/app/screens/add_reaction/index.js +++ b/app/screens/add_reaction/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {dismissModal, setButtons} from 'app/actions/navigation'; + import AddReaction from './add_reaction'; function mapStateToProps(state) { @@ -13,4 +16,13 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(AddReaction); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + dismissModal, + setButtons, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(AddReaction); diff --git a/app/screens/channel/channel.android.js b/app/screens/channel/channel.android.js index 14748150e..866911888 100644 --- a/app/screens/channel/channel.android.js +++ b/app/screens/channel/channel.android.js @@ -20,35 +20,30 @@ import ChannelBase, {ClientUpgradeListener, style} from './channel_base'; export default class ChannelAndroid extends ChannelBase { render() { const {height} = Dimensions.get('window'); - const { - navigator, - } = this.props; const channelLoaderStyle = [style.channelLoader, {height}]; const drawerContent = ( - + - + - {LocalConfig.EnableMobileClientUpgrade && } + {LocalConfig.EnableMobileClientUpgrade && } ); diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js index 700de6ab7..5e9189102 100644 --- a/app/screens/channel/channel.ios.js +++ b/app/screens/channel/channel.ios.js @@ -38,7 +38,6 @@ export default class ChannelIOS extends ChannelBase { const {height} = Dimensions.get('window'); const { currentChannelId, - navigator, } = this.props; const channelLoaderStyle = [style.channelLoader, {height}]; @@ -48,17 +47,15 @@ export default class ChannelIOS extends ChannelBase { const drawerContent = ( - + @@ -74,7 +71,7 @@ export default class ChannelIOS extends ChannelBase { height={height} style={channelLoaderStyle} /> - {LocalConfig.EnableMobileClientUpgrade && } + {LocalConfig.EnableMobileClientUpgrade && } diff --git a/app/screens/code/code.js b/app/screens/code/code.js index 831538c8a..019b3346b 100644 --- a/app/screens/code/code.js +++ b/app/screens/code/code.js @@ -18,8 +18,10 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class Code extends React.PureComponent { static propTypes = { + actions: PropTypes.shape({ + popTopScreen: PropTypes.func.isRequired, + }).isRequired, componentId: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, content: PropTypes.string.isRequired, }; @@ -39,7 +41,7 @@ export default class Code extends React.PureComponent { } handleAndroidBack = () => { - this.props.navigator.pop(); + this.props.actions.popTopScreen(); return true; }; diff --git a/app/screens/code/index.js b/app/screens/code/index.js index 4b0abdcd9..c833020be 100644 --- a/app/screens/code/index.js +++ b/app/screens/code/index.js @@ -1,10 +1,13 @@ // 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 {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {popTopScreen} from 'app/actions/navigation'; + import Code from './code'; function mapStateToProps(state) { @@ -13,4 +16,12 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(Code); \ No newline at end of file +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + popTopScreen, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Code); \ No newline at end of file diff --git a/app/screens/error_teams_list/error_teams_list.js b/app/screens/error_teams_list/error_teams_list.js index a795920c8..a972997cd 100644 --- a/app/screens/error_teams_list/error_teams_list.js +++ b/app/screens/error_teams_list/error_teams_list.js @@ -32,9 +32,9 @@ export default class ErrorTeamsList extends PureComponent { connection: PropTypes.func.isRequired, logout: PropTypes.func.isRequired, selectDefaultTeam: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, - navigator: PropTypes.object, theme: PropTypes.object, }; @@ -64,21 +64,10 @@ export default class ErrorTeamsList extends PureComponent { } goToChannelView = () => { - const {navigator, theme} = this.props; - - navigator.resetTo({ - screen: 'Channel', - animated: false, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - disableTermsModal: true, - }, - }); + const passProps = { + disableTermsModal: true, + }; + this.props.actions.resetToChannel(passProps); }; getUserInfo = async () => { diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js index 1a8c053dc..229ec1f42 100644 --- a/app/screens/error_teams_list/error_teams_list.test.js +++ b/app/screens/error_teams_list/error_teams_list.test.js @@ -22,6 +22,7 @@ describe('ErrorTeamsList', () => { connection: () => {}, // eslint-disable-line no-empty-function logout: () => {}, // eslint-disable-line no-empty-function selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function + resetToChannel: jest.fn(), }, componentId: 'component-id', theme: Preferences.THEMES.default, @@ -39,6 +40,7 @@ describe('ErrorTeamsList', () => { const selectDefaultTeam = jest.fn(); const logout = jest.fn(); const actions = { + ...baseProps.actions, loadMe, logout, selectDefaultTeam, diff --git a/app/screens/error_teams_list/index.js b/app/screens/error_teams_list/index.js index 31424ed03..981e62733 100644 --- a/app/screens/error_teams_list/index.js +++ b/app/screens/error_teams_list/index.js @@ -8,6 +8,8 @@ import {logout, loadMe} from 'mattermost-redux/actions/users'; import {connection} from 'app/actions/device'; import {selectDefaultTeam} from 'app/actions/views/select_team'; +import {resetToChannel} from 'app/actions/navigation'; + import ErrorTeamsList from './error_teams_list.js'; function mapDispatchToProps(dispatch) { @@ -17,6 +19,7 @@ function mapDispatchToProps(dispatch) { selectDefaultTeam, connection, loadMe, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/expanded_announcement_banner/expanded_announcement_banner.js b/app/screens/expanded_announcement_banner/expanded_announcement_banner.js index 363105a53..3fb7a31ae 100644 --- a/app/screens/expanded_announcement_banner/expanded_announcement_banner.js +++ b/app/screens/expanded_announcement_banner/expanded_announcement_banner.js @@ -16,15 +16,15 @@ export default class ExpandedAnnouncementBanner extends React.PureComponent { static propTypes = { actions: PropTypes.shape({ dismissBanner: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, }).isRequired, allowDismissal: PropTypes.bool.isRequired, bannerText: PropTypes.string.isRequired, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, } close = () => { - this.props.navigator.pop(); + this.props.actions.popTopScreen(); }; dismissBanner = () => { @@ -67,7 +67,6 @@ export default class ExpandedAnnouncementBanner extends React.PureComponent { { - const {getItemMeasures, navigator} = this.props; + const {actions, getItemMeasures, componentId} = this.props; const {index} = this.state; this.setState({animating: true}); - navigator.setStyle({ - screenBackgroundColor: 'transparent', + Navigation.mergeOptions(componentId, { + layout: { + backgroundColor: 'transparent', + }, }); getItemMeasures(index, (origin) => { @@ -117,7 +126,7 @@ export default class ImagePreview extends PureComponent { } this.animateOpenAnimToValue(0, () => { - navigator.dismissModal({animationType: 'none'}); + actions.dismissModal(); }); }); }; @@ -177,7 +186,7 @@ export default class ImagePreview extends PureComponent { }; renderAttachmentDocument = (file) => { - const {canDownloadFiles, theme, navigator} = this.props; + const {canDownloadFiles, theme} = this.props; return ( @@ -190,7 +199,6 @@ export default class ImagePreview extends PureComponent { file={file} iconHeight={100} iconWidth={100} - navigator={navigator} theme={theme} wrapperHeight={200} wrapperWidth={200} @@ -441,6 +449,7 @@ export default class ImagePreview extends PureComponent { }; showDownloadOptionsIOS = async () => { + const {actions} = this.props; const {formatMessage} = this.context.intl; const file = this.getCurrentFile(); const items = []; @@ -504,30 +513,17 @@ export default class ImagePreview extends PureComponent { }); } - const options = { - title: file.caption, - items, - onCancelPress: () => this.setHeaderAndFooterVisible(true), - }; - if (items.length) { this.setHeaderAndFooterVisible(false); - this.props.navigator.showModal({ - screen: 'OptionsModal', - title: '', - animationType: 'none', - passProps: { - ...options, - }, - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - modalPresentationStyle: 'overCurrentContext', - }, - }); + const screen = 'OptionsModal'; + const passProps = { + title: file.caption, + items, + onCancelPress: () => this.setHeaderAndFooterVisible(true), + }; + + actions.showModalOverCurrentContext(screen, passProps); } }; diff --git a/app/screens/image_preview/image_preview.test.js b/app/screens/image_preview/image_preview.test.js index e4e31a0f6..922e26551 100644 --- a/app/screens/image_preview/image_preview.test.js +++ b/app/screens/image_preview/image_preview.test.js @@ -6,6 +6,7 @@ import {shallow} from 'enzyme'; import { TouchableOpacity, } from 'react-native'; +import {Navigation} from 'react-native-navigation'; import Preferences from 'mattermost-redux/constants/preferences'; @@ -18,6 +19,13 @@ jest.mock('react-native-doc-viewer', () => { OpenFile: jest.fn(), }; }); +jest.mock('react-native-navigation', () => ({ + Navigation: { + mergeOptions: jest.fn(), + }, +})); + +Navigation.mergeOptions = jest.fn(); describe('ImagePreview', () => { const baseProps = { @@ -30,10 +38,14 @@ describe('ImagePreview', () => { ], getItemMeasures: jest.fn(), index: 0, - navigator: {setStyle: jest.fn()}, origin: {}, target: {}, theme: Preferences.THEMES.default, + componentId: 'component-id', + actions: { + dismissModal: jest.fn(), + showModalOverCurrentContext: jest.fn(), + }, }; test('should match snapshot', () => { @@ -67,21 +79,26 @@ describe('ImagePreview', () => { expect(wrapper.state('index')).toEqual(1); }); - test('should match call getItemMeasures & navigator.setStyle on close', () => { + test('should match call getItemMeasures & Navigation.mergeOptions on close', () => { const getItemMeasures = jest.fn(); - const navigator = {setStyle: jest.fn()}; const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, ); wrapper.instance().close(); - expect(navigator.setStyle).toHaveBeenCalledTimes(2); - expect(navigator.setStyle).toBeCalledWith({screenBackgroundColor: 'transparent'}); + expect(Navigation.mergeOptions).toHaveBeenCalledTimes(2); + expect(Navigation.mergeOptions).toHaveBeenCalledWith( + baseProps.componentId, + { + layout: { + backgroundColor: 'transparent', + }, + }, + ); }); }); diff --git a/app/screens/image_preview/index.js b/app/screens/image_preview/index.js index 425a1df99..e72f70bff 100644 --- a/app/screens/image_preview/index.js +++ b/app/screens/image_preview/index.js @@ -1,12 +1,15 @@ // 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 {getDimensions} from 'app/selectors/device'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/general'; +import {dismissModal, showModalOverCurrentContext} from 'app/actions/navigation'; +import {getDimensions} from 'app/selectors/device'; + import ImagePreview from './image_preview'; function mapStateToProps(state) { @@ -17,4 +20,13 @@ function mapStateToProps(state) { }; } -export default connect(mapStateToProps)(ImagePreview); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + dismissModal, + showModalOverCurrentContext, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(ImagePreview); diff --git a/app/screens/index.js b/app/screens/index.js index 36d38b336..498e7171a 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -9,23 +9,12 @@ import Channel from 'app/screens/channel'; import Root from 'app/components/root'; import SelectServer from 'app/screens/select_server'; -// TODO remove dummy navigator object -const navigator = { - push: () => {}, // eslint-disable-line no-empty-function - setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function - setStyle: () => {}, // eslint-disable-line no-empty-function - setButtons: () => {}, // eslint-disable-line no-empty-function -}; - export function registerScreens(store, Provider) { // TODO consolidate this with app/utils/wrap_context_provider const wrapper = (Comp) => (props) => ( // eslint-disable-line react/display-name - - + + ); diff --git a/app/screens/interactive_dialog/dialog_element.js b/app/screens/interactive_dialog/dialog_element.js index e675bcf78..5003c12ea 100644 --- a/app/screens/interactive_dialog/dialog_element.js +++ b/app/screens/interactive_dialog/dialog_element.js @@ -25,7 +25,6 @@ export default class DialogElement extends PureComponent { options: PropTypes.arrayOf(PropTypes.object), value: PropTypes.any, onChange: PropTypes.func, - navigator: PropTypes.object, theme: PropTypes.object, }; @@ -71,7 +70,6 @@ export default class DialogElement extends PureComponent { theme, dataSource, options, - navigator, } = this.props; let {maxLength} = this.props; @@ -128,7 +126,6 @@ export default class DialogElement extends PureComponent { placeholder={placeholder} showRequiredAsterisk={true} selected={this.state.selected} - navigator={navigator} roundedBorders={false} /> ); diff --git a/app/screens/interactive_dialog/index.js b/app/screens/interactive_dialog/index.js index e68b387ea..6170b7b6e 100644 --- a/app/screens/interactive_dialog/index.js +++ b/app/screens/interactive_dialog/index.js @@ -7,6 +7,8 @@ import {connect} from 'react-redux'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations'; +import {dismissModal} from 'app/actions/navigation'; + import InteractiveDialog from './interactive_dialog'; function mapStateToProps(state) { @@ -29,6 +31,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ submitInteractiveDialog, + dismissModal, }, dispatch), }; } diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js index 94ace245b..e4888ee7e 100644 --- a/app/screens/interactive_dialog/interactive_dialog.js +++ b/app/screens/interactive_dialog/interactive_dialog.js @@ -22,10 +22,10 @@ export default class InteractiveDialog extends PureComponent { elements: PropTypes.arrayOf(PropTypes.object).isRequired, notifyOnCancel: PropTypes.bool, state: PropTypes.string, - navigator: PropTypes.object, theme: PropTypes.object, actions: PropTypes.shape({ submitInteractiveDialog: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, }).isRequired, }; @@ -136,9 +136,7 @@ export default class InteractiveDialog extends PureComponent { } handleHide = () => { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); } onChange = (name, value) => { @@ -147,7 +145,7 @@ export default class InteractiveDialog extends PureComponent { } render() { - const {elements, theme, navigator} = this.props; + const {elements, theme} = this.props; const style = getStyleFromTheme(theme); return ( @@ -172,7 +170,6 @@ export default class InteractiveDialog extends PureComponent { options={e.options} value={this.state.values[e.name]} onChange={this.onChange} - navigator={navigator} theme={theme} /> ); diff --git a/app/screens/interactive_dialog/interactive_dialog.test.js b/app/screens/interactive_dialog/interactive_dialog.test.js index 4219edf33..9030af549 100644 --- a/app/screens/interactive_dialog/interactive_dialog.test.js +++ b/app/screens/interactive_dialog/interactive_dialog.test.js @@ -19,8 +19,6 @@ describe('InteractiveDialog', () => { theme: Preferences.THEMES.default, actions: { submitInteractiveDialog: jest.fn(), - }, - navigator: { dismissModal: jest.fn(), }, componentId: 'component-id', @@ -37,11 +35,9 @@ describe('InteractiveDialog', () => { }); test('should submit dialog', async () => { - const submitInteractiveDialog = jest.fn(); const wrapper = shallow( , ); @@ -53,16 +49,14 @@ describe('InteractiveDialog', () => { }; wrapper.instance().handleSubmit(); - expect(submitInteractiveDialog).toHaveBeenCalledTimes(1); - expect(submitInteractiveDialog).toHaveBeenCalledWith(dialog); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledTimes(1); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledWith(dialog); }); test('should submit dialog on cancel', async () => { - const submitInteractiveDialog = jest.fn(); const wrapper = shallow( , ); @@ -75,21 +69,19 @@ describe('InteractiveDialog', () => { }; wrapper.instance().navigationButtonPressed({buttonId: 'close-dialog'}); - expect(submitInteractiveDialog).toHaveBeenCalledTimes(1); - expect(submitInteractiveDialog).toHaveBeenCalledWith(dialog); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledTimes(1); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledWith(dialog); }); test('should not submit dialog on cancel', async () => { - const submitInteractiveDialog = jest.fn(); const wrapper = shallow( , ); wrapper.instance().navigationButtonPressed({buttonId: 'close-dialog'}); - expect(submitInteractiveDialog).not.toHaveBeenCalled(); + expect(baseProps.actions.submitInteractiveDialog).not.toHaveBeenCalled(); }); }); diff --git a/app/screens/more_dms/index.js b/app/screens/more_dms/index.js index 3427eaff8..0f92b9b0a 100644 --- a/app/screens/more_dms/index.js +++ b/app/screens/more_dms/index.js @@ -14,6 +14,8 @@ import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selector import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId, getUsers} from 'mattermost-redux/selectors/entities/users'; +import {dismissModal, setButtons} from 'app/actions/navigation'; + import MoreDirectMessages from './more_dms'; function mapStateToProps(state) { @@ -40,6 +42,8 @@ function mapDispatchToProps(dispatch) { getProfilesInTeam, searchProfiles, setChannelDisplayName, + dismissModal, + setButtons, }, dispatch), }; } diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index 0d8723af3..3f1640020 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -39,13 +39,14 @@ export default class MoreDirectMessages extends PureComponent { getProfilesInTeam: PropTypes.func.isRequired, searchProfiles: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, allProfiles: PropTypes.object.isRequired, currentDisplayName: PropTypes.string, currentTeamId: PropTypes.string.isRequired, currentUserId: PropTypes.string.isRequired, - navigator: PropTypes.object, restrictDirectMessage: PropTypes.bool.isRequired, teammateNameDisplay: PropTypes.string, theme: PropTypes.object.isRequired, @@ -108,7 +109,7 @@ export default class MoreDirectMessages extends PureComponent { } close = () => { - this.props.navigator.dismissModal({animationType: 'slide-down'}); + this.props.actions.dismissModal(); }; clearSearch = () => { @@ -325,13 +326,14 @@ export default class MoreDirectMessages extends PureComponent { }; updateNavigationButtons = (startEnabled, context = this.context) => { + const {actions, componentId} = this.props; const {formatMessage} = context.intl; - this.props.navigator.setButtons({ + actions.setButtons(componentId, { rightButtons: [{ id: START_BUTTON, - title: formatMessage({id: 'mobile.more_dms.start', defaultMessage: 'Start'}), + text: formatMessage({id: 'mobile.more_dms.start', defaultMessage: 'Start'}), showAsAction: 'always', - disabled: !startEnabled, + enabled: startEnabled, }], }); }; diff --git a/app/screens/notification/index.js b/app/screens/notification/index.js index 7674cf74a..47d22b173 100644 --- a/app/screens/notification/index.js +++ b/app/screens/notification/index.js @@ -12,6 +12,8 @@ import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selector import {getUser} from 'mattermost-redux/selectors/entities/users'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {dismissOverlay, dismissAllModals, popToRoot} from 'app/actions/navigation'; + import Notification from './notification'; function mapStateToProps(state, ownProps) { @@ -42,6 +44,9 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ loadFromPushNotification, + dismissOverlay, + dismissAllModals, + popToRoot, }, dispatch), }; } diff --git a/app/screens/notification/notification.js b/app/screens/notification/notification.js index ff2225f00..732796e12 100644 --- a/app/screens/notification/notification.js +++ b/app/screens/notification/notification.js @@ -8,55 +8,149 @@ import { InteractionManager, Platform, StyleSheet, - Text, TouchableOpacity, + Text, View, } from 'react-native'; - -import FormattedText from 'app/components/formatted_text'; -import ProfilePicture from 'app/components/profile_picture'; -import {changeOpacity} from 'app/utils/theme'; +import {Navigation} from 'react-native-navigation'; +import * as Animatable from 'react-native-animatable'; +import {PanGestureHandler} from 'react-native-gesture-handler'; import {isDirectChannel} from 'mattermost-redux/utils/channel_utils'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; +import FormattedText from 'app/components/formatted_text'; +import ProfilePicture from 'app/components/profile_picture'; +import {changeOpacity} from 'app/utils/theme'; +import {NavigationTypes} from 'app/constants'; + import logo from 'assets/images/icon.png'; import webhookIcon from 'assets/images/icons/webhook.jpg'; const IMAGE_SIZE = 33; +const AUTO_DISMISS_TIME_MILLIS = 5000; export default class Notification extends PureComponent { static propTypes = { actions: PropTypes.shape({ loadFromPushNotification: PropTypes.func.isRequired, + dismissOverlay: PropTypes.func.isRequired, + dismissAllModals: PropTypes.func.isRequired, + popToRoot: PropTypes.func.isRequired, }).isRequired, + componentId: PropTypes.string.isRequired, channel: PropTypes.object, config: PropTypes.object, deviceWidth: PropTypes.number.isRequired, notification: PropTypes.object.isRequired, teammateNameDisplay: PropTypes.string, - navigator: PropTypes.object, theme: PropTypes.object.isRequired, user: PropTypes.object, }; + state = { + keyFrames: { + from: { + translateY: -100, + }, + to: { + translateY: 0, + }, + }, + } + + tapped = false; + + componentDidMount() { + this.setDismissTimer(); + this.setDidDisappearListener(); + this.setShowOverlayListener(); + } + + componentWillUnmount() { + this.clearDismissTimer(); + this.clearDidDisappearListener(); + this.clearShowOverlayListener(); + } + + setDismissTimer = () => { + this.dismissTimer = setTimeout(() => { + if (!this.tapped) { + this.animateDismissOverlay(); + } + }, AUTO_DISMISS_TIME_MILLIS); + } + + clearDismissTimer = () => { + if (this.dismissTimer) { + clearTimeout(this.dismissTimer); + this.dismissTimer = null; + } + } + + setDidDisappearListener = () => { + this.didDismissListener = Navigation.events().registerComponentDidDisappearListener(({componentId}) => { + if (componentId === this.props.componentId && this.tapped) { + const {actions} = this.props; + actions.dismissAllModals(); + actions.popToRoot(); + } + }); + } + + clearDidDisappearListener = () => { + this.didDismissListener.remove(); + } + + setShowOverlayListener = () => { + EventEmitter.on(NavigationTypes.NAVIGATION_SHOW_OVERLAY, this.onNewOverlay); + } + + clearShowOverlayListener = () => { + EventEmitter.off(NavigationTypes.NAVIGATION_SHOW_OVERLAY, this.onNewOverlay); + } + + onNewOverlay = () => { + // Dismiss this overlay so that there is only ever one. + this.dismissOverlay(); + } + + dismissOverlay = () => { + this.clearDismissTimer(); + + const {actions, componentId} = this.props; + actions.dismissOverlay(componentId); + } + + animateDismissOverlay = () => { + this.clearDismissTimer(); + + this.setState({ + keyFrames: { + from: { + translateY: 0, + }, + to: { + translateY: -100, + }, + }, + }); + setTimeout(() => this.dismissOverlay(), 1000); + } + notificationTapped = () => { - const {actions, navigator, notification} = this.props; + this.tapped = true; + this.clearDismissTimer(); + + const {actions, notification} = this.props; EventEmitter.emit('close_channel_drawer'); + EventEmitter.emit('close_settings_sidebar'); InteractionManager.runAfterInteractions(() => { - navigator.dismissInAppNotification(); + this.dismissOverlay(); if (!notification.localNotification) { actions.loadFromPushNotification(notification); - - if (Platform.OS === 'android') { - navigator.dismissModal({animation: 'none'}); - } - - navigator.popToRoot({ - animated: false, - }); } }); }; @@ -202,28 +296,39 @@ export default class Notification extends PureComponent { const icon = this.getNotificationIcon(); return ( - - + - - {icon} + + + + {icon} + + + {title} + + + {messageText} + + + + - - {title} - - - {messageText} - - - - - + + ); } diff --git a/app/screens/permalink/index.js b/app/screens/permalink/index.js index 50d54399f..ef5f06525 100644 --- a/app/screens/permalink/index.js +++ b/app/screens/permalink/index.js @@ -16,13 +16,18 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import { + goToScreen, + dismissModal, + dismissAllModals, + resetToChannel, +} from 'app/actions/navigation'; import { handleSelectChannel, loadThreadIfNecessary, setChannelDisplayName, setChannelLoading, } from 'app/actions/views/channel'; -import {showSearchModal} from 'app/actions/views/search'; import {handleTeamChange} from 'app/actions/views/select_team'; import Permalink from './permalink'; @@ -74,7 +79,10 @@ function mapDispatchToProps(dispatch) { selectPost, setChannelDisplayName, setChannelLoading, - showSearchModal, + goToScreen, + dismissModal, + dismissAllModals, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js index d223b031b..3d59012b0 100644 --- a/app/screens/permalink/permalink.js +++ b/app/screens/permalink/permalink.js @@ -57,6 +57,10 @@ export default class Permalink extends PureComponent { selectPost: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, setChannelLoading: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + dismissAllModals: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, channelId: PropTypes.string, channelIsArchived: PropTypes.bool, @@ -67,7 +71,6 @@ export default class Permalink extends PureComponent { focusedPostId: PropTypes.string.isRequired, isPermalink: PropTypes.bool, myMembers: PropTypes.object.isRequired, - navigator: PropTypes.object, onClose: PropTypes.func, onPress: PropTypes.func, postIds: PropTypes.array, @@ -171,39 +174,29 @@ export default class Permalink extends PureComponent { } goToThread = preventDoubleTap((post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, + }; actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, - }; - - navigator.push(options); + actions.goToScreen(screen, title, passProps); }); handleClose = () => { - const {actions, navigator, onClose} = this.props; + const {actions, onClose} = this.props; if (this.refs.view) { this.mounted = false; this.refs.view.zoomOut().then(() => { actions.selectPost(''); - navigator.dismissModal({animationType: 'none'}); + actions.dismissModal(); if (onClose) { onClose(); @@ -232,7 +225,7 @@ export default class Permalink extends PureComponent { jumpToChannel = (channelId, channelDisplayName) => { if (channelId) { - const {actions, channelTeamId, currentTeamId, navigator, onClose, theme} = this.props; + const {actions, channelTeamId, currentTeamId, onClose} = this.props; const currentChannelId = this.props.channelId; const { handleSelectChannel, @@ -246,23 +239,13 @@ export default class Permalink extends PureComponent { if (channelId === currentChannelId) { EventEmitter.emit('reset_channel'); } else { - navigator.resetTo({ - screen: 'Channel', - animated: true, - animationType: 'fade', - navigatorStyle: { - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - disableTermsModal: true, - }, - }); + const passProps = { + disableTermsModal: true, + }; + actions.resetToChannel(passProps); } - navigator.dismissAllModals({animationType: 'slide-down'}); + actions.dismissAllModals(); if (onClose) { onClose(); @@ -350,7 +333,6 @@ export default class Permalink extends PureComponent { const { currentUserId, focusedPostId, - navigator, theme, } = this.props; const { @@ -395,7 +377,6 @@ export default class Permalink extends PureComponent { lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(postIdsState) : -1} currentUserId={currentUserId} lastViewedAt={0} - navigator={navigator} highlightPinnedOrFlagged={false} /> ); diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js index 5cc33b09c..bd1147c5a 100644 --- a/app/screens/permalink/permalink.test.js +++ b/app/screens/permalink/permalink.test.js @@ -11,13 +11,6 @@ import Permalink from './permalink.js'; jest.mock('react-intl'); describe('Permalink', () => { - const navigator = { - dismissAllModals: jest.fn(), - dismissModal: jest.fn(), - push: jest.fn(), - resetTo: jest.fn(), - }; - const actions = { getPostsAround: jest.fn(), getPostThread: jest.fn(), @@ -29,6 +22,10 @@ describe('Permalink', () => { selectPost: jest.fn(), setChannelDisplayName: jest.fn(), setChannelLoading: jest.fn(), + goToScreen: jest.fn(), + dismissModal: jest.fn(), + dismissAllModals: jest.fn(), + resetToChannel: jest.fn(), }; const baseProps = { @@ -42,7 +39,6 @@ describe('Permalink', () => { focusedPostId: 'focused_post_id', isPermalink: true, myMembers: {}, - navigator, onClose: jest.fn(), onPress: jest.fn(), postIds: ['post_id_1', 'focused_post_id', 'post_id_3'], diff --git a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap index bb73b1098..bdfdeb767 100644 --- a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap +++ b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap @@ -22,7 +22,7 @@ exports[`ReactionList should match snapshot 1`] = ` } } > - - - - { - this.props.navigator.dismissModal({ - animationType: 'none', - }); + this.props.actions.dismissModal(); }; getMissingProfiles = () => { @@ -137,7 +135,6 @@ export default class ReactionList extends PureComponent { renderReactionRows = () => { const { - navigator, teammateNameDisplay, theme, } = this.props; @@ -157,7 +154,6 @@ export default class ReactionList extends PureComponent { > { const baseProps = { actions: { getMissingProfilesByIds: jest.fn(), + dismissModal: jest.fn(), }, allUserIds: ['user_id_1', 'user_id_2'], reactions: {'user_id_1-smile': {emoji_name: 'smile', user_id: 'user_id_1'}, 'user_id_2-+1': {emoji_name: '+1', user_id: 'user_id_2'}}, diff --git a/app/screens/reaction_list/__snapshots__/reaction_row.test.js.snap b/app/screens/reaction_list/reaction_row/__snapshots__/reaction_row.test.js.snap similarity index 100% rename from app/screens/reaction_list/__snapshots__/reaction_row.test.js.snap rename to app/screens/reaction_list/reaction_row/__snapshots__/reaction_row.test.js.snap diff --git a/app/screens/reaction_list/reaction_row/index.js b/app/screens/reaction_list/reaction_row/index.js new file mode 100644 index 000000000..d91992b1f --- /dev/null +++ b/app/screens/reaction_list/reaction_row/index.js @@ -0,0 +1,19 @@ +// 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 {goToScreen} from 'app/actions/navigation'; + +import ReactionRow from './reaction_row'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + goToScreen, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(ReactionRow); diff --git a/app/screens/reaction_list/reaction_row.js b/app/screens/reaction_list/reaction_row/reaction_row.js similarity index 82% rename from app/screens/reaction_list/reaction_row.js rename to app/screens/reaction_list/reaction_row/reaction_row.js index da33b73ff..8ad6e2e56 100644 --- a/app/screens/reaction_list/reaction_row.js +++ b/app/screens/reaction_list/reaction_row/reaction_row.js @@ -21,8 +21,10 @@ import Emoji from 'app/components/emoji'; export default class ReactionRow extends React.PureComponent { static propTypes = { + actions: PropTypes.shape({ + goToScreen: PropTypes.func.isRequired, + }).isRequired, emojiName: PropTypes.string.isRequired, - navigator: PropTypes.object, teammateNameDisplay: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, user: PropTypes.object.isRequired, @@ -37,26 +39,15 @@ export default class ReactionRow extends React.PureComponent { }; goToUserProfile = () => { - const {navigator, theme, user} = this.props; + const {actions, user} = this.props; const {formatMessage} = this.context.intl; - - const options = { - screen: 'UserProfile', - title: formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}), - animated: true, - backButtonTitle: '', - passProps: { - userId: user.id, - }, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, + const screen = 'UserProfile'; + const title = formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'}); + const passProps = { + userId: user.id, }; - navigator.push(options); + actions.goToScreen(screen, title, passProps); }; render() { diff --git a/app/screens/reaction_list/reaction_row.test.js b/app/screens/reaction_list/reaction_row/reaction_row.test.js similarity index 91% rename from app/screens/reaction_list/reaction_row.test.js rename to app/screens/reaction_list/reaction_row/reaction_row.test.js index 471b67658..21e7fcd10 100644 --- a/app/screens/reaction_list/reaction_row.test.js +++ b/app/screens/reaction_list/reaction_row/reaction_row.test.js @@ -9,8 +9,10 @@ import ReactionRow from './reaction_row'; describe('ReactionRow', () => { const baseProps = { + actions: { + goToScreen: jest.fn(), + }, emojiName: 'smile', - navigator: {}, teammateNameDisplay: 'username', theme: Preferences.THEMES.default, user: {id: 'user_id', username: 'username'}, diff --git a/app/screens/search/index.js b/app/screens/search/index.js index a8751a9f6..7f6918eae 100644 --- a/app/screens/search/index.js +++ b/app/screens/search/index.js @@ -15,7 +15,7 @@ import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; -import {dismissModal} from 'app/actions/navigation'; +import {dismissModal, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation'; import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/channel'; import {handleSearchDraftChanged} from 'app/actions/views/search'; import {isLandscape} from 'app/selectors/device'; @@ -86,6 +86,8 @@ function mapDispatchToProps(dispatch) { getMorePostsForSearch, selectPost, dismissModal, + goToScreen, + showModalOverCurrentContext, }, dispatch), }; } diff --git a/app/screens/search/search.js b/app/screens/search/search.js index 82acb7e3e..d8d32a2f7 100644 --- a/app/screens/search/search.js +++ b/app/screens/search/search.js @@ -57,12 +57,13 @@ export default class Search extends PureComponent { selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, dismissModal: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, + showModalOverCurrentContext: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string.isRequired, currentTeamId: PropTypes.string.isRequired, initialValue: PropTypes.string, isLandscape: PropTypes.bool.isRequired, - navigator: PropTypes.object, postIds: PropTypes.array, archivedPostIds: PropTypes.arrayOf(PropTypes.string), recent: PropTypes.array.isRequired, @@ -183,7 +184,7 @@ export default class Search extends PureComponent { }); goToThread = (post) => { - const {actions, navigator, theme} = this.props; + const {actions} = this.props; const channelId = post.channel_id; const rootId = (post.root_id || post.id); @@ -191,23 +192,14 @@ export default class Search extends PureComponent { actions.loadThreadIfNecessary(rootId); actions.selectPost(rootId); - const options = { - screen: 'Thread', - animated: true, - backButtonTitle: '', - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - channelId, - rootId, - }, + const screen = 'Thread'; + const title = ''; + const passProps = { + channelId, + rootId, }; - navigator.push(options); + actions.goToScreen(screen, title, passProps); }; handleHashtagPress = (hashtag) => { @@ -389,7 +381,6 @@ export default class Search extends PureComponent { postId={item} previewPost={this.previewPost} goToThread={this.goToThread} - navigator={this.props.navigator} onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={mattermostManaged.getCachedConfig()} @@ -449,29 +440,24 @@ export default class Search extends PureComponent { }; showPermalinkView = (postId, isPermalink) => { - const {actions, navigator} = this.props; + const {actions} = this.props; actions.selectFocusedPostId(postId); if (!this.showingPermalink) { + const screen = 'Permalink'; + const passProps = { + isPermalink, + onClose: this.handleClosePermalink, + }; const options = { - screen: 'Permalink', - animationType: 'none', - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: changeOpacity('#000', 0.2), - modalPresentationStyle: 'overCurrentContext', - }, - passProps: { - isPermalink, - onClose: this.handleClosePermalink, + layout: { + backgroundColor: changeOpacity('#000', 0.2), }, }; this.showingPermalink = true; - navigator.showModal(options); + actions.showModalOverCurrentContext(screen, passProps, options); } }; diff --git a/app/screens/selector_screen/index.js b/app/screens/selector_screen/index.js index e5f69d1fd..1bd00b4ac 100644 --- a/app/screens/selector_screen/index.js +++ b/app/screens/selector_screen/index.js @@ -9,6 +9,8 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getProfiles, searchProfiles} from 'mattermost-redux/actions/users'; import {getChannels, searchChannels} from 'mattermost-redux/actions/channels'; +import {popTopScreen} from 'app/actions/navigation'; + import SelectorScreen from './selector_screen'; function mapStateToProps(state) { @@ -32,6 +34,7 @@ function mapDispatchToProps(dispatch) { getChannels, searchProfiles, searchChannels, + popTopScreen, }, dispatch), }; } diff --git a/app/screens/selector_screen/selector_screen.js b/app/screens/selector_screen/selector_screen.js index 2694641e7..9db461989 100644 --- a/app/screens/selector_screen/selector_screen.js +++ b/app/screens/selector_screen/selector_screen.js @@ -34,12 +34,12 @@ export default class SelectorScreen extends PureComponent { getChannels: PropTypes.func.isRequired, searchProfiles: PropTypes.func.isRequired, searchChannels: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, }), componentId: PropTypes.string, currentTeamId: PropTypes.string.isRequired, data: PropTypes.arrayOf(PropTypes.object), dataSource: PropTypes.string, - navigator: PropTypes.object, onSelect: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, }; @@ -93,7 +93,7 @@ export default class SelectorScreen extends PureComponent { }; close = () => { - this.props.navigator.pop({animated: true}); + this.props.actions.popTopScreen(); }; handleSelectItem = (id, item) => { diff --git a/app/screens/selector_screen/selector_screen.test.js b/app/screens/selector_screen/selector_screen.test.js index b017809f9..eb6d311cb 100644 --- a/app/screens/selector_screen/selector_screen.test.js +++ b/app/screens/selector_screen/selector_screen.test.js @@ -67,6 +67,7 @@ describe('SelectorScreen', () => { getChannels, searchProfiles, searchChannels, + popTopScreen: jest.fn(), }; const baseProps = { diff --git a/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap b/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap index f323d4cbd..313792907 100644 --- a/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap +++ b/app/screens/terms_of_service/__snapshots__/terms_of_service.test.js.snap @@ -45,87 +45,6 @@ exports[`TermsOfService should enable/disable navigator buttons on setNavigatorB disableAtMentions={true} disableChannelLink={true} disableHashtags={true} - navigator={ - Object { - "dismissAllModals": [MockFunction], - "dismissModal": [MockFunction], - "setButtons": [MockFunction] { - "calls": Array [ - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": false, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": false, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } textStyles={ Object { "code": Object { @@ -261,110 +180,6 @@ exports[`TermsOfService should enable/disable navigator buttons on setNavigatorB disableAtMentions={true} disableChannelLink={true} disableHashtags={true} - navigator={ - Object { - "dismissAllModals": [MockFunction], - "dismissModal": [MockFunction], - "setButtons": [MockFunction] { - "calls": Array [ - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": false, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": false, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } textStyles={ Object { "code": Object { @@ -572,87 +387,6 @@ exports[`TermsOfService should match snapshot on enableNavigatorLogout 1`] = ` disableAtMentions={true} disableChannelLink={true} disableHashtags={true} - navigator={ - Object { - "dismissAllModals": [MockFunction], - "dismissModal": [MockFunction], - "setButtons": [MockFunction] { - "calls": Array [ - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": true, - "icon": Object {}, - "id": "reject-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - Array [ - Object { - "leftButtons": Array [ - Object { - "disabled": false, - "icon": Object {}, - "id": "close-terms-of-service", - }, - ], - "rightButtons": Array [ - Object { - "disabled": true, - "id": "accept-terms-of-service", - "showAsAction": "always", - "title": undefined, - }, - ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } textStyles={ Object { "code": Object { diff --git a/app/screens/terms_of_service/index.js b/app/screens/terms_of_service/index.js index a9c843bac..51519d733 100644 --- a/app/screens/terms_of_service/index.js +++ b/app/screens/terms_of_service/index.js @@ -8,6 +8,12 @@ import {getTermsOfService, logout, updateMyTermsOfServiceStatus} from 'mattermos import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import { + setButtons, + dismissModal, + dismissAllModals, +} from 'app/actions/navigation'; + import TermsOfService from './terms_of_service.js'; function mapStateToProps(state) { @@ -25,6 +31,9 @@ function mapDispatchToProps(dispatch) { getTermsOfService, logout, updateMyTermsOfServiceStatus, + setButtons, + dismissModal, + dismissAllModals, }, dispatch), }; } diff --git a/app/screens/terms_of_service/terms_of_service.js b/app/screens/terms_of_service/terms_of_service.js index 850b36e57..14fb41968 100644 --- a/app/screens/terms_of_service/terms_of_service.js +++ b/app/screens/terms_of_service/terms_of_service.js @@ -36,10 +36,12 @@ export default class TermsOfService extends PureComponent { logout: PropTypes.func.isRequired, getTermsOfService: PropTypes.func.isRequired, updateMyTermsOfServiceStatus: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + dismissAllModals: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, closeButton: PropTypes.object, - navigator: PropTypes.object, siteName: PropTypes.string, theme: PropTypes.object, }; @@ -71,7 +73,7 @@ export default class TermsOfService extends PureComponent { termsText: '', }; - this.rightButton.title = context.intl.formatMessage({id: 'terms_of_service.agreeButton', defaultMessage: 'I Agree'}); + this.rightButton.text = context.intl.formatMessage({id: 'terms_of_service.agreeButton', defaultMessage: 'I Agree'}); this.leftButton.icon = props.closeButton; this.setNavigatorButtons(false); @@ -106,27 +108,28 @@ export default class TermsOfService extends PureComponent { } setNavigatorButtons = (enabled = true) => { + const {actions, componentId} = this.props; const buttons = { - leftButtons: [{...this.leftButton, disabled: !enabled}], - rightButtons: [{...this.rightButton, disabled: !enabled}], + leftButtons: [{...this.leftButton, enabled}], + rightButtons: [{...this.rightButton, enabled}], }; - this.props.navigator.setButtons(buttons); + actions.setButtons(componentId, buttons); }; enableNavigatorLogout = () => { const buttons = { - leftButtons: [{...this.leftButton, id: 'close-terms-of-service', disabled: false}], - rightButtons: [{...this.rightButton, disabled: true}], + leftButtons: [{...this.leftButton, id: 'close-terms-of-service', enabled: true}], + rightButtons: [{...this.rightButton, enabled: false}], }; - this.props.navigator.setButtons(buttons); + this.props.actions.setButtons(buttons); }; closeTermsAndLogout = () => { const {actions} = this.props; - this.props.navigator.dismissAllModals(); + actions.dismissAllModals(); actions.logout(); }; @@ -163,9 +166,7 @@ export default class TermsOfService extends PureComponent { this.registerUserAction( true, () => { - this.props.navigator.dismissModal({ - animationType: 'slide-down', - }); + this.props.actions.dismissModal(); }, this.handleAcceptTerms ); @@ -234,7 +235,7 @@ export default class TermsOfService extends PureComponent { }; render() { - const {navigator, theme} = this.props; + const {theme} = this.props; const styles = getStyleSheet(theme); const blockStyles = getMarkdownBlockStyles(theme); @@ -267,7 +268,6 @@ export default class TermsOfService extends PureComponent { > { getTermsOfService: jest.fn(), updateMyTermsOfServiceStatus: jest.fn(), logout: jest.fn(), + setButtons: jest.fn(), + dismissModal: jest.fn(), + dismissAllModals: jest.fn(), }; const baseProps = { actions, - navigator: { - dismissAllModals: jest.fn(), - dismissModal: jest.fn(), - setButtons: jest.fn(), - }, theme: Preferences.THEMES.default, closeButton: {}, siteName: 'Mattermost', @@ -83,18 +81,18 @@ describe('TermsOfService', () => { expect(wrapper.getElement()).toMatchSnapshot(); }); - test('should call props.navigator.setButtons on setNavigatorButtons', async () => { + test('should call props.actions.setButtons on setNavigatorButtons', async () => { const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, ); wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'}); - expect(baseProps.navigator.setButtons).toHaveBeenCalledTimes(2); + expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(2); wrapper.instance().setNavigatorButtons(true); - expect(baseProps.navigator.setButtons).toHaveBeenCalledTimes(3); + expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(3); wrapper.instance().setNavigatorButtons(false); - expect(baseProps.navigator.setButtons).toHaveBeenCalledTimes(4); + expect(baseProps.actions.setButtons).toHaveBeenCalledTimes(4); }); test('should enable/disable navigator buttons on setNavigatorButtons true/false', () => { @@ -129,6 +127,6 @@ describe('TermsOfService', () => { wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'}); wrapper.instance().closeTermsAndLogout(); - expect(baseProps.navigator.dismissAllModals).toHaveBeenCalledTimes(1); + expect(baseProps.actions.dismissAllModals).toHaveBeenCalledTimes(1); }); }); diff --git a/app/screens/text_preview/text_preview.js b/app/screens/text_preview/text_preview.js index 68e7b214b..daa637fa7 100644 --- a/app/screens/text_preview/text_preview.js +++ b/app/screens/text_preview/text_preview.js @@ -18,7 +18,6 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut export default class TextPreview extends React.PureComponent { static propTypes = { componentId: PropTypes.string, - navigator: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, content: PropTypes.string.isRequired, }; diff --git a/app/screens/thread/__snapshots__/thread.ios.test.js.snap b/app/screens/thread/__snapshots__/thread.ios.test.js.snap index ebef67766..897c3640e 100644 --- a/app/screens/thread/__snapshots__/thread.ios.test.js.snap +++ b/app/screens/thread/__snapshots__/thread.ios.test.js.snap @@ -13,28 +13,6 @@ exports[`thread should match snapshot, has root post 1`] = ` lastPostIndex={2} lastViewedAt={0} location="thread" - navigator={ - Object { - "dismissModal": [MockFunction], - "pop": [MockFunction], - "resetTo": [MockFunction], - "setTitle": [MockFunction] { - "calls": Array [ - Array [ - Object { - "title": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } onPostPress={[Function]} postIds={ Array [ @@ -76,28 +54,6 @@ exports[`thread should match snapshot, has root post 1`] = ` channelId="channel_id" channelIsArchived={false} cursorPositionEvent="onThreadTextBoxCursorChange" - navigator={ - Object { - "dismissModal": [MockFunction], - "pop": [MockFunction], - "resetTo": [MockFunction], - "setTitle": [MockFunction] { - "calls": Array [ - Array [ - Object { - "title": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } onCloseChannel={[Function]} rootId="root_id" valueEvent="onThreadTextBoxValueChange" @@ -128,28 +84,6 @@ exports[`thread should match snapshot, render footer 1`] = ` lastPostIndex={2} lastViewedAt={0} location="thread" - navigator={ - Object { - "dismissModal": [MockFunction], - "pop": [MockFunction], - "resetTo": [MockFunction], - "setTitle": [MockFunction] { - "calls": Array [ - Array [ - Object { - "title": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } onPostPress={[Function]} postIds={ Array [ @@ -176,28 +110,6 @@ exports[`thread should match snapshot, render footer 2`] = ` lastPostIndex={2} lastViewedAt={0} location="thread" - navigator={ - Object { - "dismissModal": [MockFunction], - "pop": [MockFunction], - "resetTo": [MockFunction], - "setTitle": [MockFunction] { - "calls": Array [ - Array [ - Object { - "title": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - } - } onPostPress={[Function]} postIds={ Array [ diff --git a/app/screens/thread/index.js b/app/screens/thread/index.js index 7f1573ab0..a089d7755 100644 --- a/app/screens/thread/index.js +++ b/app/screens/thread/index.js @@ -10,6 +10,8 @@ import {selectPost} from 'mattermost-redux/actions/posts'; import {makeGetChannel, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels'; import {makeGetPostIdsForThread} from 'mattermost-redux/selectors/entities/posts'; +import {popTopScreen, resetToChannel} from 'app/actions/navigation'; + import Thread from './thread'; function makeMapStateToProps() { @@ -37,6 +39,8 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ selectPost, + popTopScreen, + resetToChannel, }, dispatch), }; } diff --git a/app/screens/thread/thread.android.js b/app/screens/thread/thread.android.js index 2184e0d88..849c3a2fa 100644 --- a/app/screens/thread/thread.android.js +++ b/app/screens/thread/thread.android.js @@ -19,7 +19,6 @@ export default class ThreadAndroid extends ThreadBase { const { channelId, myMember, - navigator, postIds, rootId, channelIsArchived, @@ -36,7 +35,6 @@ export default class ThreadAndroid extends ThreadBase { currentUserId={myMember && myMember.user_id} lastViewedAt={this.state.lastViewedAt} lastPostIndex={-1} - navigator={navigator} onPostPress={this.hideKeyboard} location={THREAD} /> @@ -47,7 +45,6 @@ export default class ThreadAndroid extends ThreadBase { channelIsArchived={channelIsArchived} rootId={rootId} channelId={channelId} - navigator={navigator} onCloseChannel={this.onCloseChannel} /> ); diff --git a/app/screens/thread/thread.ios.js b/app/screens/thread/thread.ios.js index 7ba12e368..863717d26 100644 --- a/app/screens/thread/thread.ios.js +++ b/app/screens/thread/thread.ios.js @@ -29,7 +29,6 @@ export default class ThreadIOS extends ThreadBase { const { channelId, myMember, - navigator, postIds, rootId, channelIsArchived, @@ -47,7 +46,6 @@ export default class ThreadIOS extends ThreadBase { lastPostIndex={getLastPostIndex(postIds)} currentUserId={myMember && myMember.user_id} lastViewedAt={this.state.lastViewedAt} - navigator={navigator} onPostPress={this.hideKeyboard} location={THREAD} scrollViewNativeID={SCROLLVIEW_NATIVE_ID} @@ -77,7 +75,6 @@ export default class ThreadIOS extends ThreadBase { channelIsArchived={channelIsArchived} rootId={rootId} channelId={channelId} - navigator={navigator} onCloseChannel={this.onCloseChannel} cursorPositionEvent={THREAD_POST_TEXTBOX_CURSOR_CHANGE} valueEvent={THREAD_POST_TEXTBOX_VALUE_CHANGE} diff --git a/app/screens/thread/thread.ios.test.js b/app/screens/thread/thread.ios.test.js index 309b71088..0478ed22d 100644 --- a/app/screens/thread/thread.ios.test.js +++ b/app/screens/thread/thread.ios.test.js @@ -11,22 +11,22 @@ import PostList from 'app/components/post_list'; import ThreadIOS from './thread.ios'; jest.mock('react-intl'); +jest.mock('react-native-navigation', () => ({ + Navigation: { + mergeOptions: jest.fn(), + }, +})); describe('thread', () => { - const navigator = { - dismissModal: jest.fn(), - pop: jest.fn(), - resetTo: jest.fn(), - setTitle: jest.fn(), - }; const baseProps = { actions: { selectPost: jest.fn(), + popTopScreen: jest.fn(), + resetToChannel: jest.fn(), }, channelId: 'channel_id', channelType: General.OPEN_CHANNEL, displayName: 'channel_display_name', - navigator, myMember: {last_viewed_at: 0, user_id: 'member_user_id'}, rootId: 'root_id', theme: Preferences.THEMES.default, @@ -55,35 +55,19 @@ describe('thread', () => { expect(wrapper.getElement()).toMatchSnapshot(); }); - test('should call props.navigator on onCloseChannel', () => { - const channelScreen = { - screen: 'Channel', - title: '', - animated: false, - backButtonTitle: '', - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - }, - passProps: { - disableTermsModal: true, - }, + test('should call props.actions.resetToChannel on onCloseChannel', () => { + const passProps = { + disableTermsModal: true, }; - const newNavigator = {...navigator}; const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, ); wrapper.instance().onCloseChannel(); - expect(newNavigator.resetTo).toHaveBeenCalledTimes(1); - expect(newNavigator.resetTo).toBeCalledWith(channelScreen); + expect(baseProps.actions.resetToChannel).toHaveBeenCalledTimes(1); + expect(baseProps.actions.resetToChannel).toBeCalledWith(passProps); }); test('should match snapshot, render footer', () => { diff --git a/app/screens/thread/thread_base.js b/app/screens/thread/thread_base.js index 468142ebd..0b64e7e32 100644 --- a/app/screens/thread/thread_base.js +++ b/app/screens/thread/thread_base.js @@ -3,8 +3,9 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {Keyboard, Platform} from 'react-native'; +import {Keyboard} from 'react-native'; import {intlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {General, RequestStatus} from 'mattermost-redux/constants'; @@ -16,12 +17,13 @@ export default class ThreadBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ selectPost: PropTypes.func.isRequired, + popTopScreen: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, channelId: PropTypes.string.isRequired, channelType: PropTypes.string, displayName: PropTypes.string, - navigator: PropTypes.object, myMember: PropTypes.object.isRequired, rootId: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, @@ -53,8 +55,12 @@ export default class ThreadBase extends PureComponent { this.postTextbox = React.createRef(); - this.props.navigator.setTitle({ - title, + Navigation.mergeOptions(props.componentId, { + topBar: { + title: { + text: title, + }, + }, }); this.state = { @@ -82,17 +88,7 @@ export default class ThreadBase extends PureComponent { } close = () => { - const {navigator} = this.props; - - if (Platform.OS === 'ios') { - navigator.pop({ - animated: true, - }); - } else { - navigator.dismissModal({ - animationType: 'slide-down', - }); - } + this.props.actions.popTopScreen(); }; handleAutoComplete = (value) => { @@ -124,22 +120,9 @@ export default class ThreadBase extends PureComponent { }; onCloseChannel = () => { - this.props.navigator.resetTo({ - screen: 'Channel', - title: '', - animated: false, - backButtonTitle: '', - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: 'transparent', - }, - passProps: { - disableTermsModal: true, - }, - }); + const passProps = { + disableTermsModal: true, + }; + this.props.actions.resetToChannel(passProps); }; } diff --git a/app/screens/user_profile/index.js b/app/screens/user_profile/index.js index adeb16750..f4e926c61 100644 --- a/app/screens/user_profile/index.js +++ b/app/screens/user_profile/index.js @@ -15,6 +15,13 @@ import {loadBot} from 'mattermost-redux/actions/bots'; import {getBotAccounts} from 'mattermost-redux/selectors/entities/bots'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import { + setButtons, + dismissModal, + resetToChannel, + goToScreen, +} from 'app/actions/navigation'; + import UserProfile from './user_profile'; function mapStateToProps(state, ownProps) { @@ -43,6 +50,10 @@ function mapDispatchToProps(dispatch) { makeDirectChannel, setChannelDisplayName, loadBot, + setButtons, + dismissModal, + resetToChannel, + goToScreen, }, dispatch), }; } diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index 130ffd3a1..848e8332d 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -33,11 +33,14 @@ export default class UserProfile extends PureComponent { makeDirectChannel: PropTypes.func.isRequired, setChannelDisplayName: PropTypes.func.isRequired, loadBot: PropTypes.func.isRequired, + setButtons: PropTypes.func.isRequired, + dismissModal: PropTypes.func.isRequired, + resetToChannel: PropTypes.func.isRequired, + goToScreen: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string, config: PropTypes.object.isRequired, currentDisplayName: PropTypes.string, - navigator: PropTypes.object, teammateNameDisplay: PropTypes.string, theme: PropTypes.object.isRequired, user: PropTypes.object.isRequired, @@ -61,13 +64,13 @@ export default class UserProfile extends PureComponent { super(props); if (props.isMyUser) { - this.rightButton.title = context.intl.formatMessage({id: 'mobile.routes.user_profile.edit', defaultMessage: 'Edit'}); + this.rightButton.text = context.intl.formatMessage({id: 'mobile.routes.user_profile.edit', defaultMessage: 'Edit'}); const buttons = { rightButtons: [this.rightButton], }; - props.navigator.setButtons(buttons); + props.actions.setButtons(props.componentId, buttons); } } @@ -97,30 +100,17 @@ export default class UserProfile extends PureComponent { } close = () => { - const {navigator, theme} = this.props; + const {actions, fromSettings} = this.props; - if (this.props.fromSettings) { - navigator.dismissModal({ - animationType: 'slide-down', - }); + if (fromSettings) { + actions.dismissModal(); return; } - navigator.resetTo({ - screen: 'Channel', - animated: true, - navigatorStyle: { - animated: true, - animationType: 'fade', - navBarHidden: true, - statusBarHidden: false, - statusBarHideWithNavBar: false, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - disableTermsModal: true, - }, - }); + const passProps = { + disableTermsModal: true, + }; + actions.resetToChannel(passProps); }; getDisplayName = () => { @@ -231,27 +221,15 @@ export default class UserProfile extends PureComponent { }; goToEditProfile = () => { - const {user: currentUser} = this.props; + const {actions, user: currentUser} = this.props; const {formatMessage} = this.context.intl; const commandType = 'Push'; - - const {navigator, theme} = this.props; - const options = { - screen: 'EditProfile', - title: formatMessage({id: 'mobile.routes.edit_profile', defaultMessage: 'Edit Profile'}), - animated: true, - backButtonTitle: '', - passProps: {currentUser, commandType}, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - }; + const screen = 'EditProfile'; + const title = formatMessage({id: 'mobile.routes.edit_profile', defaultMessage: 'Edit Profile'}); + const passProps = {currentUser, commandType}; requestAnimationFrame(() => { - navigator.push(options); + actions.goToScreen(screen, title, passProps); }); }; diff --git a/app/screens/user_profile/user_profile.test.js b/app/screens/user_profile/user_profile.test.js index af4e8d903..5c425058f 100644 --- a/app/screens/user_profile/user_profile.test.js +++ b/app/screens/user_profile/user_profile.test.js @@ -22,6 +22,10 @@ describe('user_profile', () => { setChannelDisplayName: jest.fn(), makeDirectChannel: jest.fn(), loadBot: jest.fn(), + setButtons: jest.fn(), + dismissModal: jest.fn(), + resetToChannel: jest.fn(), + goToScreen: jest.fn(), }; const baseProps = { actions, @@ -29,11 +33,6 @@ describe('user_profile', () => { ShowEmailAddress: true, }, teammateNameDisplay: 'username', - navigator: { - resetTo: jest.fn(), - push: jest.fn(), - dismissModal: jest.fn(), - }, teams: [], theme: Preferences.THEMES.default, enableTimezone: false, @@ -90,16 +89,9 @@ describe('user_profile', () => { }); test('should push EditProfile', async () => { - const props = { - ...baseProps, - navigator: { - push: jest.fn(), - }, - }; - const wrapper = shallow( , {context: {intl: {formatMessage: jest.fn()}}}, @@ -107,18 +99,18 @@ describe('user_profile', () => { wrapper.instance().goToEditProfile(); setTimeout(() => { - expect(props.navigator.push).toHaveBeenCalledTimes(1); + expect(baseProps.actions.goToScreen).toHaveBeenCalledTimes(1); }, 16); }); test('should call goToEditProfile', () => { const props = { ...baseProps, - navigator: { - push: jest.fn(), + actions: { + ...baseProps.actions, + goToScreen: jest.fn(), }, }; - const wrapper = shallow( { const event = {buttonId: wrapper.instance().rightButton.id}; wrapper.instance().navigationButtonPressed(event); setTimeout(() => { - expect(props.navigator.push).toHaveBeenCalledTimes(1); + expect(baseProps.actions.goToScreen).toHaveBeenCalledTimes(1); }, 0); }); @@ -147,11 +139,11 @@ describe('user_profile', () => { const event = {buttonId: 'close-settings'}; wrapper.instance().navigationButtonPressed(event); - expect(props.navigator.dismissModal).toHaveBeenCalledTimes(1); + expect(props.actions.dismissModal).toHaveBeenCalledTimes(1); props.fromSettings = false; wrapper.setProps({...props}); wrapper.instance().navigationButtonPressed(event); - expect(props.navigator.resetTo).toHaveBeenCalledTimes(1); + expect(props.actions.resetToChannel).toHaveBeenCalledTimes(1); }); }); diff --git a/app/utils/push_notifications.js b/app/utils/push_notifications.js index 97a46032e..0214bbdd8 100644 --- a/app/utils/push_notifications.js +++ b/app/utils/push_notifications.js @@ -15,6 +15,7 @@ import { createPostForNotificationReply, loadFromPushNotification, } from 'app/actions/views/root'; +import {dismissAllModals, popToRoot} from 'app/actions/navigation'; import {ViewTypes} from 'app/constants'; import {getLocalizedMessage} from 'app/i18n'; import {getCurrentServerUrl, getAppCredentials} from 'app/init/credentials'; @@ -45,7 +46,13 @@ class PushNotificationUtils { await this.store.dispatch(loadFromPushNotification(notification, true)); if (!EphemeralStore.appStartedFromPushNotification) { - EventEmitter.emit(ViewTypes.NOTIFICATION_TAPPED); + EventEmitter.emit('close_channel_drawer'); + EventEmitter.emit('close_settings_sidebar'); + + const {dispatch} = this.store; + dispatch(dismissAllModals()); + dispatch(popToRoot()); + PushNotifications.resetNotification(); } }; diff --git a/app/utils/wrap_context_provider.js b/app/utils/wrap_context_provider.js index 015d9378a..cea58cabb 100644 --- a/app/utils/wrap_context_provider.js +++ b/app/utils/wrap_context_provider.js @@ -6,10 +6,8 @@ import IntlWrapper from 'app/components/root'; export function wrapWithContextProvider(Comp, excludeEvents = true) { return (props) => { //eslint-disable-line react/display-name - const {navigator} = props; //eslint-disable-line react/prop-types return ( diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0830f40fa..de555aa2b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 63cb0d0aef536c8cbf15bf664fcfe31852a13dd1 -COCOAPODS: 1.6.1 +COCOAPODS: 1.5.3 From 5838d5163f7968767baad836fb6a83753125a722 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 9 Jul 2019 07:51:11 -0700 Subject: [PATCH 17/19] Wrap setting show true in requestAnimationFrame (#2964) --- app/components/sidebars/main/main_sidebar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/sidebars/main/main_sidebar.js b/app/components/sidebars/main/main_sidebar.js index deb649a42..edb8d6ddd 100644 --- a/app/components/sidebars/main/main_sidebar.js +++ b/app/components/sidebars/main/main_sidebar.js @@ -143,7 +143,7 @@ export default class ChannelSidebar extends Component { }; handleShowDrawerContent = () => { - this.setState({show: true}); + requestAnimationFrame(() => this.setState({show: true})); }; closeChannelDrawer = () => { From f5a9c7df81c9321883ccd859d3cece4600946eb6 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 9 Jul 2019 14:21:52 -0700 Subject: [PATCH 18/19] Fix Makefile indentation --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c75f80686..6027aa303 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ run-android: | check-device-android pre-run prepare-android-build ## Runs the ap @if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \ echo Starting React Native packager server; \ npm start & echo Running Android app in development; \ - if [ ! -z ${VARIANT} ]; then \ + if [ ! -z ${VARIANT} ]; then \ react-native run-android --no-packager --variant=${VARIANT}; \ else \ react-native run-android --no-packager; \ From c30bc8bfea0e4f37fef28fd446a54f9dd063c6e5 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Fri, 12 Jul 2019 08:56:36 -0700 Subject: [PATCH 19/19] Check for null config --- .../main/java/com/mattermost/rnbeta/MainApplication.java | 5 +---- .../rnbeta/ManagedActivityLifecycleCallbacks.java | 6 +++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java index c089baeee..eb952447d 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java @@ -127,10 +127,7 @@ public class MainApplication extends NavigationApplication implements INotificat super.onCreate(); instance = this; - // TODO: Right now I get a black screen when resuming the app from the - // background if these callbacks are registered. I'm commenting this out - // to move forward and will come back to this at a later date. - //registerActivityLifecycleCallbacks(new ManagedActivityLifecycleCallbacks()); + registerActivityLifecycleCallbacks(new ManagedActivityLifecycleCallbacks()); // Delete any previous temp files created by the app File tempFolder = new File(getApplicationContext().getCacheDir(), "mmShare"); diff --git a/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java b/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java index a3fdfe59f..cebf7a187 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/ManagedActivityLifecycleCallbacks.java @@ -17,6 +17,7 @@ import java.util.Set; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; public class ManagedActivityLifecycleCallbacks implements ActivityLifecycleCallbacks { @@ -96,7 +97,10 @@ public class ManagedActivityLifecycleCallbacks implements ActivityLifecycleCallb } private void sendConfigChanged(Bundle config) { - Object result = Arguments.fromBundle(config); + WritableMap result = Arguments.createMap(); + if (config != null) { + result = Arguments.fromBundle(config); + } ReactContext ctx = MainApplication.instance.getRunningReactContext(); if (ctx != null) {