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];