diff --git a/app/actions/views/search.js b/app/actions/views/search.js index 2ccd0060d..2726e6635 100644 --- a/app/actions/views/search.js +++ b/app/actions/views/search.js @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + import {ViewTypes} from 'app/constants'; export function handleSearchDraftChanged(text) { @@ -11,3 +13,25 @@ export function handleSearchDraftChanged(text) { }, getState); }; } + +export function showSearchModal(navigator, initialValue = '') { + return (dispatch, getState) => { + const theme = getTheme(getState()); + + const options = { + screen: 'Search', + animated: true, + backButtonTitle: '', + overrideBackPress: true, + passProps: { + initialValue, + }, + navigatorStyle: { + navBarHidden: true, + screenBackgroundColor: theme.centerChannelBg, + }, + }; + + navigator.showModal(options); + }; +} diff --git a/app/components/markdown/__snapshots__/hashtag.test.js.snap b/app/components/markdown/hashtag/__snapshots__/hashtag.test.js.snap similarity index 100% rename from app/components/markdown/__snapshots__/hashtag.test.js.snap rename to app/components/markdown/hashtag/__snapshots__/hashtag.test.js.snap diff --git a/app/components/markdown/hashtag.js b/app/components/markdown/hashtag/hashtag.js similarity index 65% rename from app/components/markdown/hashtag.js rename to app/components/markdown/hashtag/hashtag.js index 424f58420..75fc5486f 100644 --- a/app/components/markdown/hashtag.js +++ b/app/components/markdown/hashtag/hashtag.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import {intlShape} from 'react-intl'; import {Text} from 'react-native'; import CustomPropTypes from 'app/constants/custom_prop_types'; @@ -14,37 +13,23 @@ export default class Hashtag extends React.PureComponent { linkStyle: CustomPropTypes.Style.isRequired, onHashtagPress: PropTypes.func, navigator: PropTypes.object.isRequired, - theme: PropTypes.object.isRequired, - }; - - static contextTypes = { - intl: intlShape, + actions: PropTypes.shape({ + showSearchModal: PropTypes.func.isRequired, + }).isRequired, }; handlePress = () => { if (this.props.onHashtagPress) { this.props.onHashtagPress(this.props.hashtag); + return; } - const options = { - screen: 'Search', - animated: true, - backButtonTitle: '', - passProps: { - initialValue: '#' + this.props.hashtag, - }, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: this.props.theme.centerChannelBg, - }, - }; - // Close thread view, permalink view, etc this.props.navigator.dismissAllModals(); this.props.navigator.popToRoot(); - this.props.navigator.showModal(options); + this.props.actions.showSearchModal(this.props.navigator, '#' + this.props.hashtag); }; render() { diff --git a/app/components/markdown/hashtag.test.js b/app/components/markdown/hashtag/hashtag.test.js similarity index 67% rename from app/components/markdown/hashtag.test.js rename to app/components/markdown/hashtag/hashtag.test.js index 9caae2c39..05d87e3dd 100644 --- a/app/components/markdown/hashtag.test.js +++ b/app/components/markdown/hashtag/hashtag.test.js @@ -11,8 +11,13 @@ describe('Hashtag', () => { const baseProps = { hashtag: 'test', linkStyle: {color: 'red'}, - navigator: {}, - theme: {}, + navigator: { + dismissAllModals: jest.fn(), + popToRoot: jest.fn(), + }, + actions: { + showSearchModal: jest.fn(), + }, }; test('should match snapshot', () => { @@ -24,11 +29,6 @@ describe('Hashtag', () => { test('should open hashtag search on click', () => { const props = { ...baseProps, - navigator: { - dismissAllModals: jest.fn(), - popToRoot: jest.fn(), - showModal: jest.fn(), - }, }; const wrapper = shallow(); @@ -37,22 +37,12 @@ describe('Hashtag', () => { expect(props.navigator.dismissAllModals).toHaveBeenCalled(); expect(props.navigator.popToRoot).toHaveBeenCalled(); - expect(props.navigator.showModal).toHaveBeenCalledWith(expect.objectContaining({ - screen: 'Search', - passProps: { - initialValue: '#test', - }, - })); + expect(props.actions.showSearchModal).toHaveBeenCalledWith(props.navigator, '#test'); }); test('should call onHashtagPress if provided', () => { const props = { ...baseProps, - navigator: { - dismissAllModals: jest.fn(), - popToRoot: jest.fn(), - showModal: jest.fn(), - }, onHashtagPress: jest.fn(), }; @@ -62,7 +52,7 @@ describe('Hashtag', () => { expect(props.navigator.dismissAllModals).not.toBeCalled(); expect(props.navigator.popToRoot).not.toBeCalled(); - expect(props.navigator.showModal).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 new file mode 100644 index 000000000..3683f71ab --- /dev/null +++ b/app/components/markdown/hashtag/index.js @@ -0,0 +1,19 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; + +import {showSearchModal} from 'app/actions/views/search'; + +import Hashtag from './hashtag'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + showSearchModal, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(Hashtag); diff --git a/app/components/markdown/markdown.js b/app/components/markdown/markdown.js index 47c7076e5..0ee784551 100644 --- a/app/components/markdown/markdown.js +++ b/app/components/markdown/markdown.js @@ -232,7 +232,6 @@ export default class Markdown extends PureComponent { linkStyle={this.props.textStyles.link} onHashtagPress={this.props.onHashtagPress} navigator={this.props.navigator} - theme={this.props.theme} /> ); } diff --git a/app/components/post_list/post_list_base.js b/app/components/post_list/post_list_base.js index 5ccf6723d..92df8ef90 100644 --- a/app/components/post_list/post_list_base.js +++ b/app/components/post_list/post_list_base.js @@ -31,6 +31,7 @@ export default class PostListBase extends PureComponent { lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types navigator: PropTypes.object, onLoadMoreUp: PropTypes.func, + onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func, onPostPress: PropTypes.func, onRefresh: PropTypes.func, @@ -156,6 +157,7 @@ export default class PostListBase extends PureComponent { highlightPostId, isSearchResult, navigator, + onHashtagPress, onPostPress, renderReplies, shouldRenderReplyButton, @@ -168,6 +170,7 @@ export default class PostListBase extends PureComponent { postId={postId} previousPostId={previousPostId} nextPostId={nextPostId} + onHashtagPress={onHashtagPress} onPermalinkPress={this.handlePermalinkPress} highlight={highlight} renderReplies={renderReplies} @@ -212,7 +215,6 @@ export default class PostListBase extends PureComponent { passProps: { isPermalink: true, onClose: this.handleClosePermalink, - onPermalinkPress: this.handlePermalinkPress, }, }; diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js index af1a30556..67132b393 100644 --- a/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js +++ b/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js @@ -12,34 +12,21 @@ import AwesomeIcon from 'react-native-vector-icons/FontAwesome'; import {preventDoubleTap} from 'app/utils/tap'; import {makeStyleSheetFromTheme} from 'app/utils/theme'; -const SEARCH = 'search'; - export default class ChannelSearchButton extends PureComponent { static propTypes = { actions: PropTypes.shape({ clearSearch: PropTypes.func.isRequired, - handlePostDraftChanged: PropTypes.func.isRequired, + showSearchModal: PropTypes.func.isRequired, }).isRequired, navigator: PropTypes.object, theme: PropTypes.object, }; handlePress = preventDoubleTap(async () => { - const {actions, navigator, theme} = this.props; + const {actions, navigator} = this.props; await actions.clearSearch(); - actions.handlePostDraftChanged(SEARCH, ''); - - navigator.showModal({ - screen: 'Search', - animated: true, - backButtonTitle: '', - overrideBackPress: true, - navigatorStyle: { - navBarHidden: true, - screenBackgroundColor: theme.centerChannelBg, - }, - }); + await actions.showSearchModal(navigator); }); render() { diff --git a/app/screens/channel/channel_nav_bar/channel_search_button/index.js b/app/screens/channel/channel_nav_bar/channel_search_button/index.js index 7f566689d..12e7f3d09 100644 --- a/app/screens/channel/channel_nav_bar/channel_search_button/index.js +++ b/app/screens/channel/channel_nav_bar/channel_search_button/index.js @@ -6,7 +6,7 @@ import {connect} from 'react-redux'; import {clearSearch} from 'mattermost-redux/actions/search'; -import {handlePostDraftChanged} from 'app/actions/views/channel'; +import {showSearchModal} from 'app/actions/views/search'; import ChannelSearchButton from './channel_search_button'; @@ -14,7 +14,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ clearSearch, - handlePostDraftChanged, + showSearchModal, }, dispatch), }; } diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index a70b75b10..76ba03382 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -308,7 +308,6 @@ export default class ChannelInfo extends PureComponent { passProps: { isPermalink: true, onClose: this.handleClosePermalink, - onPermalinkPress: this.handlePermalinkPress, }, }; diff --git a/app/screens/flagged_posts/flagged_posts.js b/app/screens/flagged_posts/flagged_posts.js index badef8365..5df34eb7c 100644 --- a/app/screens/flagged_posts/flagged_posts.js +++ b/app/screens/flagged_posts/flagged_posts.js @@ -33,6 +33,7 @@ export default class FlaggedPosts extends PureComponent { getFlaggedPosts: PropTypes.func.isRequired, selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, + showSearchModal: PropTypes.func.isRequired, }).isRequired, didFail: PropTypes.bool, isLoading: PropTypes.bool, @@ -112,6 +113,14 @@ export default class FlaggedPosts extends PureComponent { this.showPermalinkView(postId, true); }; + handleHashtagPress = async (hashtag) => { + const {actions, navigator} = this.props; + + await navigator.dismissModal(); + + actions.showSearchModal(navigator, '#' + hashtag); + }; + keyExtractor = (item) => item; onNavigatorEvent = (event) => { @@ -173,6 +182,7 @@ export default class FlaggedPosts extends PureComponent { previewPost={this.previewPost} goToThread={this.goToThread} navigator={this.props.navigator} + onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={managedConfig} showFullDate={false} @@ -212,7 +222,6 @@ export default class FlaggedPosts extends PureComponent { passProps: { isPermalink, onClose: this.handleClosePermalink, - onPermalinkPress: this.handlePermalinkPress, }, }; diff --git a/app/screens/flagged_posts/index.js b/app/screens/flagged_posts/index.js index 57a9337f2..16d3446f8 100644 --- a/app/screens/flagged_posts/index.js +++ b/app/screens/flagged_posts/index.js @@ -10,6 +10,7 @@ import {RequestStatus} from 'mattermost-redux/constants'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; 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'; @@ -40,6 +41,7 @@ function mapDispatchToProps(dispatch) { getFlaggedPosts, selectFocusedPostId, selectPost, + showSearchModal, }, dispatch), }; } diff --git a/app/screens/permalink/index.js b/app/screens/permalink/index.js index 7dc02212c..b38552dfd 100644 --- a/app/screens/permalink/index.js +++ b/app/screens/permalink/index.js @@ -18,6 +18,7 @@ import { setChannelDisplayName, setChannelLoading, } from 'app/actions/views/channel'; +import {showSearchModal} from 'app/actions/views/search'; import {handleTeamChange} from 'app/actions/views/select_team'; import Permalink from './permalink'; @@ -72,6 +73,7 @@ function mapDispatchToProps(dispatch) { selectPost, setChannelDisplayName, setChannelLoading, + showSearchModal, }, dispatch), }; } diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js index 7291aa5a1..47b23717c 100644 --- a/app/screens/permalink/permalink.js +++ b/app/screens/permalink/permalink.js @@ -70,8 +70,6 @@ export default class Permalink extends PureComponent { myMembers: PropTypes.object.isRequired, navigator: PropTypes.object, onClose: PropTypes.func, - onHashtagPress: PropTypes.func, - onPermalinkPress: PropTypes.func, onPress: PropTypes.func, postIds: PropTypes.array, theme: PropTypes.object.isRequired, @@ -209,6 +207,14 @@ export default class Permalink extends PureComponent { } }; + handleHashtagPress = () => { + // Do nothing because we're already in a modal + }; + + handlePermalinkPress = () => { + // Do nothing because we're already in permalink view for a different post + }; + handlePress = () => { const {channelIdState, channelNameState} = this.state; @@ -359,8 +365,6 @@ export default class Permalink extends PureComponent { currentUserId, focusedPostId, navigator, - onHashtagPress, - onPermalinkPress, theme, } = this.props; const { @@ -398,8 +402,8 @@ export default class Permalink extends PureComponent { isSearchResult={false} shouldRenderReplyButton={false} renderReplies={true} - onHashtagPress={onHashtagPress} - onPermalinkPress={onPermalinkPress} + onHashtagPress={this.handleHashtagPress} + onPermalinkPress={this.handlePermalinkPress} onPostPress={this.goToThread} postIds={postIdsState} currentUserId={currentUserId} diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js index 4f7ad2657..1f66ce64f 100644 --- a/app/screens/permalink/permalink.test.js +++ b/app/screens/permalink/permalink.test.js @@ -48,8 +48,6 @@ describe('Permalink', () => { myMembers: {}, navigator, onClose: jest.fn(), - onHashtagPress: jest.fn(), - onPermalinkPress: jest.fn(), onPress: jest.fn(), postIds: ['post_id_1', 'focused_post_id', 'post_id_3'], theme: Preferences.THEMES.default, diff --git a/app/screens/recent_mentions/index.js b/app/screens/recent_mentions/index.js index 83198213c..914e756f2 100644 --- a/app/screens/recent_mentions/index.js +++ b/app/screens/recent_mentions/index.js @@ -10,6 +10,7 @@ import {RequestStatus} from 'mattermost-redux/constants'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; 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'; @@ -40,6 +41,7 @@ function mapDispatchToProps(dispatch) { getRecentMentions, selectFocusedPostId, selectPost, + showSearchModal, }, dispatch), }; } diff --git a/app/screens/recent_mentions/recent_mentions.js b/app/screens/recent_mentions/recent_mentions.js index bc99b65d1..4651c229a 100644 --- a/app/screens/recent_mentions/recent_mentions.js +++ b/app/screens/recent_mentions/recent_mentions.js @@ -33,6 +33,7 @@ export default class RecentMentions extends PureComponent { getRecentMentions: PropTypes.func.isRequired, selectFocusedPostId: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, + showSearchModal: PropTypes.func.isRequired, }).isRequired, didFail: PropTypes.bool, isLoading: PropTypes.bool, @@ -112,6 +113,14 @@ export default class RecentMentions extends PureComponent { this.showPermalinkView(postId, true); }; + handleHashtagPress = async (hashtag) => { + const {actions, navigator} = this.props; + + await navigator.dismissModal(); + + actions.showSearchModal(navigator, '#' + hashtag); + }; + keyExtractor = (item) => item; onNavigatorEvent = (event) => { @@ -173,6 +182,7 @@ export default class RecentMentions extends PureComponent { previewPost={this.previewPost} goToThread={this.goToThread} navigator={this.props.navigator} + onHashtagPress={this.handleHashtagPress} onPermalinkPress={this.handlePermalinkPress} managedConfig={managedConfig} showFullDate={false} @@ -212,7 +222,6 @@ export default class RecentMentions extends PureComponent { passProps: { isPermalink, onClose: this.handleClosePermalink, - onPermalinkPress: this.handlePermalinkPress, }, }; diff --git a/app/screens/search/search.js b/app/screens/search/search.js index 145252f33..fac99e0b8 100644 --- a/app/screens/search/search.js +++ b/app/screens/search/search.js @@ -518,8 +518,6 @@ export default class Search extends PureComponent { passProps: { isPermalink, onClose: this.handleClosePermalink, - onHashtagPress: this.handleHashtagPress, - onPermalinkPress: this.handlePermalinkPress, }, }; 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 be62a3132..b7d1668f0 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,7 @@ export default class SearchResultPost extends PureComponent { goToThread: PropTypes.func.isRequired, managedConfig: PropTypes.object.isRequired, navigator: PropTypes.object.isRequired, - onHashtagPress: PropTypes.func.isRequired, + onHashtagPress: PropTypes.func, onPermalinkPress: PropTypes.func.isRequired, postId: PropTypes.string.isRequired, previewPost: PropTypes.func.isRequired,