MM-29252 Tapping on invalid permalink shows an error (#4926)

This commit is contained in:
Elias Nahum 2020-10-28 11:54:47 -03:00 committed by GitHub
parent e5bf198f0e
commit 2c0b67067f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 155 additions and 305 deletions

View file

@ -46,15 +46,21 @@ export function loadChannelsByTeamName(teamName, errorHandler) {
return async (dispatch, getState) => {
const state = getState();
const {currentTeamId} = state.entities.teams;
const team = getTeamByName(state, teamName);
if (!team && errorHandler) {
errorHandler();
if (teamName) {
const team = getTeamByName(state, teamName);
if (!team && errorHandler) {
errorHandler();
return {error: true};
}
if (team && team.id !== currentTeamId) {
await dispatch(fetchMyChannelsAndMembers(team.id));
}
}
if (team && team.id !== currentTeamId) {
await dispatch(fetchMyChannelsAndMembers(team.id));
}
return {data: true};
};
}

View file

@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {intlShape} from 'react-intl';
import {Keyboard} from 'react-native';
import {showModalOverCurrentContext} from '@actions/navigation';
import {loadChannelsByTeamName} from '@actions/views/channel';
import {selectFocusedPostId} from '@mm-redux/actions/posts';
import type {DispatchFunc} from '@mm-redux/types/actions';
import {permalinkBadTeam} from '@utils/general';
import {changeOpacity} from '@utils/theme';
export let showingPermalink = false;
export function showPermalink(intl: typeof intlShape, teamName: string, postId: string, openAsPermalink = true) {
return async (dispatch: DispatchFunc) => {
const loadTeam = await dispatch(loadChannelsByTeamName(teamName, permalinkBadTeam.bind(null, intl)));
if (!loadTeam.error) {
Keyboard.dismiss();
dispatch(selectFocusedPostId(postId));
if (!showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink: openAsPermalink,
onClose: () => {
dispatch(closePermalink());
},
};
const options = {
layout: {
componentBackgroundColor: changeOpacity('#000', 0.2),
},
};
showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
}
};
}
export function closePermalink() {
return async (dispatch: DispatchFunc) => {
showingPermalink = false;
return dispatch(selectFocusedPostId(''));
};
}

View file

@ -16,7 +16,7 @@ const renderUsername = (value = '') => {
};
const renderMessage = (postBodyProps, styles, intl, localeHolder, values, skipMarkdown = false) => {
const {onPress} = postBodyProps;
const {onPress, onPermalinkPress} = postBodyProps;
const {messageStyle, textStyles} = styles;
if (skipMarkdown) {
@ -32,6 +32,7 @@ const renderMessage = (postBodyProps, styles, intl, localeHolder, values, skipMa
baseTextStyle={messageStyle}
disableAtChannelMentionHighlight={true}
onPostPress={onPress}
onPermalinkPress={onPermalinkPress}
textStyles={textStyles}
value={intl.formatMessage(localeHolder, values)}
/>

View file

@ -4,12 +4,12 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {selectFocusedPostId} from '@mm-redux/actions/posts';
import {closePermalink, showPermalink} from '@actions/views/permalink';
import {getConfig, getCurrentUrl} from '@mm-redux/selectors/entities/general';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {makePreparePostIdsForPostList, START_OF_NEW_MESSAGES} from '@mm-redux/utils/post_list';
import {handleSelectChannelByName, loadChannelsByTeamName, refreshChannelWithRetry} from 'app/actions/views/channel';
import {handleSelectChannelByName, refreshChannelWithRetry} from 'app/actions/views/channel';
import {setDeepLinkURL} from 'app/actions/views/root';
import PostList from './post_list';
@ -37,11 +37,11 @@ function makeMapStateToProps() {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
closePermalink,
handleSelectChannelByName,
loadChannelsByTeamName,
refreshChannelWithRetry,
selectFocusedPostId,
setDeepLinkURL,
showPermalink,
}, dispatch),
};
}

View file

@ -15,10 +15,8 @@ import Post from 'app/components/post';
import {DeepLinkTypes, ListTypes, NavigationTypes} from '@constants';
import mattermostManaged from 'app/mattermost_managed';
import {makeExtraData} from 'app/utils/list_view';
import {changeOpacity} from 'app/utils/theme';
import {matchDeepLink} from 'app/utils/url';
import telemetry from 'app/telemetry';
import {showModalOverCurrentContext} from 'app/actions/navigation';
import {alertErrorWithFallback} from 'app/utils/general';
import {t} from 'app/utils/i18n';
@ -42,11 +40,11 @@ const SCROLL_POSITION_CONFIG = {
export default class PostList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
closePermalink: PropTypes.func.isRequired,
handleSelectChannelByName: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
refreshChannelWithRetry: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
setDeepLinkURL: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}).isRequired,
channelId: PropTypes.string,
deepLinkURL: PropTypes.string,
@ -60,7 +58,6 @@ export default class PostList extends PureComponent {
loadMorePostsVisible: PropTypes.bool,
onLoadMoreUp: PropTypes.func,
onHashtagPress: PropTypes.func,
onPermalinkPress: PropTypes.func,
onPostPress: PropTypes.func,
onRefresh: PropTypes.func,
postIds: PropTypes.array.isRequired,
@ -171,10 +168,8 @@ export default class PostList extends PureComponent {
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
this.props.actions.closePermalink();
}
handleContentSizeChange = (contentWidth, contentHeight, forceLoad) => {
this.contentHeight = contentHeight;
@ -220,14 +215,9 @@ export default class PostList extends PureComponent {
handlePermalinkPress = (postId, teamName) => {
telemetry.start(['post_list:permalink']);
const {actions, onPermalinkPress} = this.props;
const {showPermalink} = this.props.actions;
if (onPermalinkPress) {
onPermalinkPress(postId, true);
} else {
actions.loadChannelsByTeamName(teamName, this.permalinkBadTeam);
this.showPermalinkView(postId);
}
showPermalink(this.context.intl, teamName, postId);
};
handleRefresh = () => {
@ -286,16 +276,6 @@ export default class PostList extends PureComponent {
});
};
permalinkBadTeam = () => {
const {intl} = this.context;
const message = {
id: t('mobile.server_link.unreachable_team.error'),
defaultMessage: 'This link belongs to a deleted team or to a team to which you do not have access.',
};
alertErrorWithFallback(intl, {}, message);
};
permalinkBadChannel = () => {
const {intl} = this.context;
const message = {
@ -452,29 +432,6 @@ export default class PostList extends PureComponent {
}
};
showPermalinkView = (postId, error = '') => {
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink: true,
onClose: this.handleClosePermalink,
error,
};
const options = {
layout: {
componentBackgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
};
registerViewableItemsListener = (listener) => {
this.onViewableItemsChangedListener = listener;
const removeListener = () => {

View file

@ -7,7 +7,6 @@ import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import EventEmitter from '@mm-redux/utils/event_emitter';
import * as NavigationActions from 'app/actions/navigation';
import {NavigationTypes} from '@constants';
import PostList from './post_list';
@ -20,10 +19,10 @@ describe('PostList', () => {
const baseProps = {
actions: {
closePermalink: jest.fn(),
handleSelectChannelByName: jest.fn(),
loadChannelsByTeamName: jest.fn(),
refreshChannelWithRetry: jest.fn(),
selectFocusedPostId: jest.fn(),
showPermalink: jest.fn(),
setDeepLinkURL: jest.fn(),
},
channelId: 'channel-id',
@ -49,15 +48,13 @@ describe('PostList', () => {
});
test('setting permalink deep link', () => {
const showModalOverCurrentContext = jest.spyOn(NavigationActions, 'showModalOverCurrentContext');
const wrapper = shallow(
<PostList {...baseProps}/>,
);
wrapper.setProps({deepLinkURL: deepLinks.permalink});
expect(baseProps.actions.setDeepLinkURL).toHaveBeenCalled();
expect(baseProps.actions.selectFocusedPostId).toHaveBeenCalled();
expect(showModalOverCurrentContext).toHaveBeenCalled();
expect(baseProps.actions.showPermalink).toHaveBeenCalled();
expect(wrapper.getElement()).toMatchSnapshot();
});

View file

@ -10,8 +10,10 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {dismissModal, showModalOverCurrentContext} from '@actions/navigation';
import {dismissModal} from '@actions/navigation';
import StatusBar from '@components/status_bar';
import {alertErrorWithFallback} from '@utils/general';
import {t} from '@utils/i18n';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import AddMembers from './add_members';
@ -32,10 +34,9 @@ export default class ChannelInfo extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
getChannelStats: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
getCustomEmojisInText: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}),
currentChannel: PropTypes.object.isRequired,
currentChannelCreatorName: PropTypes.string,
@ -80,34 +81,18 @@ export default class ChannelInfo extends PureComponent {
dismissModal();
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handlePermalinkPress = (postId, teamName) => {
this.props.actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId);
this.props.actions.showPermalink(this.context.intl, teamName, postId);
};
showPermalinkView = (postId) => {
const {actions} = this.props;
const screen = 'Permalink';
const passProps = {
isPermalink: true,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
permalinkBadTeam = () => {
const {intl} = this.context;
const message = {
id: t('mobile.server_link.unreachable_team.error'),
defaultMessage: 'This link belongs to a deleted team or to a team to which you do not have access.',
};
actions.selectFocusedPostId(postId);
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
alertErrorWithFallback(intl, {}, message);
};
actionsRows = (channelIsArchived) => {

View file

@ -52,9 +52,8 @@ describe('channelInfo', () => {
isLandscape: false,
actions: {
getChannelStats: jest.fn(),
loadChannelsByTeamName: jest.fn(),
getCustomEmojisInText: jest.fn(),
selectFocusedPostId: jest.fn(),
showPermalink: jest.fn(),
setChannelDisplayName: jest.fn(),
},
};

View file

@ -4,10 +4,10 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {loadChannelsByTeamName, setChannelDisplayName} from '@actions/views/channel';
import {setChannelDisplayName} from '@actions/views/channel';
import {showPermalink} from '@actions/views/permalink';
import {getChannelStats} from '@mm-redux/actions/channels';
import {getCustomEmojisInText} from '@mm-redux/actions/emojis';
import {selectFocusedPostId} from '@mm-redux/actions/posts';
import {General} from '@mm-redux/constants';
import {getTeammateNameDisplaySetting, getTheme} from '@mm-redux/selectors/entities/preferences';
import {getCurrentChannel, getCurrentChannelStats} from '@mm-redux/selectors/entities/channels';
@ -69,10 +69,9 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getChannelStats,
loadChannelsByTeamName,
getCustomEmojisInText,
selectFocusedPostId,
setChannelDisplayName,
showPermalink,
}, dispatch),
};
}

View file

@ -12,34 +12,26 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {dismissModal, goToScreen, showSearchModal} from '@actions/navigation';
import ChannelLoader from '@components/channel_loader';
import DateHeader from '@components/post_list/date_header';
import FailedNetworkAction from '@components/failed_network_action';
import NoResults from '@components/no_results';
import PostSeparator from '@components/post_separator';
import StatusBar from '@components/status_bar';
import {isDateLine, getDateForDateLine} from '@mm-redux/utils/post_list';
import ChannelLoader from 'app/components/channel_loader';
import DateHeader from 'app/components/post_list/date_header';
import FailedNetworkAction from 'app/components/failed_network_action';
import NoResults from 'app/components/no_results';
import PostSeparator from 'app/components/post_separator';
import StatusBar from 'app/components/status_bar';
import SearchResultPost from '@screens/search/search_result_post';
import ChannelDisplayName from '@screens/search/channel_display_name';
import mattermostManaged from 'app/mattermost_managed';
import SearchResultPost from 'app/screens/search/search_result_post';
import ChannelDisplayName from 'app/screens/search/channel_display_name';
import {changeOpacity} from 'app/utils/theme';
import {
goToScreen,
showModalOverCurrentContext,
showSearchModal,
dismissModal,
} from 'app/actions/navigation';
export default class FlaggedPosts extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
clearSearch: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
getPostThread: PropTypes.func.isRequired,
getFlaggedPosts: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}).isRequired,
postIds: PropTypes.array,
theme: PropTypes.object.isRequired,
@ -108,15 +100,8 @@ export default class FlaggedPosts extends PureComponent {
goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handlePermalinkPress = (postId, teamName) => {
this.props.actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId, true);
this.props.actions.showPermalink(this.context.intl, teamName, postId);
};
handleHashtagPress = async (hashtag) => {
@ -187,28 +172,6 @@ export default class FlaggedPosts extends PureComponent {
);
};
showPermalinkView = (postId, isPermalink) => {
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
};
retry = () => {
this.getFlaggedPosts();
};

View file

@ -14,10 +14,9 @@ describe('FlaggedPosts', () => {
const baseProps = {
actions: {
clearSearch: jest.fn(),
loadChannelsByTeamName: jest.fn(),
getPostThread: jest.fn(),
getFlaggedPosts: jest.fn(),
selectFocusedPostId: jest.fn(),
showPermalink: jest.fn(),
selectPost: jest.fn(),
},
theme: Preferences.THEMES.default,

View file

@ -4,9 +4,9 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {loadChannelsByTeamName} from '@actions/views/channel';
import {showPermalink} from '@actions/views/permalink';
import {getPostThread} from '@actions/views/post';
import {selectFocusedPostId, selectPost} from '@mm-redux/actions/posts';
import {selectPost} from '@mm-redux/actions/posts';
import {clearSearch, getFlaggedPosts} from '@mm-redux/actions/search';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {makePreparePostIdsForSearchPosts} from '@selectors/post_list';
@ -29,11 +29,10 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
clearSearch,
loadChannelsByTeamName,
getPostThread,
getFlaggedPosts,
selectFocusedPostId,
selectPost,
showPermalink,
}, dispatch),
};
}

View file

@ -170,10 +170,6 @@ export default class Permalink extends PureComponent {
// Do nothing because we're already in a modal
};
handlePermalinkPress = () => {
// Do nothing because we're already in permalink view for a different post
};
handlePress = () => {
if (this.viewRef) {
this.viewRef.growOut().then(() => {
@ -322,7 +318,6 @@ export default class Permalink extends PureComponent {
shouldRenderReplyButton={false}
renderReplies={true}
onHashtagPress={this.handleHashtagPress}
onPermalinkPress={this.handlePermalinkPress}
onPostPress={this.goToThread}
postIds={postIds}
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(postIds || []) : -1}

View file

@ -4,9 +4,9 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {loadChannelsByTeamName} from '@actions/views/channel';
import {showPermalink} from '@actions/views/permalink';
import {getPostThread} from '@actions/views/post';
import {selectFocusedPostId, selectPost} from '@mm-redux/actions/posts';
import {selectPost} from '@mm-redux/actions/posts';
import {clearSearch, getPinnedPosts} from '@mm-redux/actions/search';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {makePreparePostIdsForSearchPosts} from '@selectors/post_list';
@ -31,11 +31,10 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
clearSearch,
loadChannelsByTeamName,
getPostThread,
getPinnedPosts,
selectFocusedPostId,
selectPost,
showPermalink,
}, dispatch),
};
}

View file

@ -12,12 +12,7 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {
goToScreen,
showModalOverCurrentContext,
showSearchModal,
dismissModal,
} from '@actions/navigation';
import {dismissModal, goToScreen, showSearchModal} from '@actions/navigation';
import ChannelLoader from '@components/channel_loader';
import DateHeader from '@components/post_list/date_header';
import FailedNetworkAction from '@components/failed_network_action';
@ -26,7 +21,6 @@ import PostSeparator from '@components/post_separator';
import StatusBar from '@components/status_bar';
import {isDateLine, getDateForDateLine} from '@mm-redux/utils/post_list';
import SearchResultPost from '@screens/search/search_result_post';
import {changeOpacity} from '@utils/theme';
import mattermostManaged from 'app/mattermost_managed';
@ -34,11 +28,10 @@ export default class PinnedPosts extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
clearSearch: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
getPostThread: PropTypes.func.isRequired,
getPinnedPosts: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}).isRequired,
currentChannelId: PropTypes.string.isRequired,
postIds: PropTypes.array,
@ -108,15 +101,8 @@ export default class PinnedPosts extends PureComponent {
goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handlePermalinkPress = (postId, teamName) => {
this.props.actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId, true);
this.props.actions.showPermalink(this.context.intl, teamName, postId);
};
handleHashtagPress = async (hashtag) => {
@ -127,9 +113,7 @@ export default class PinnedPosts extends PureComponent {
keyExtractor = (item) => item;
previewPost = (post) => {
Keyboard.dismiss();
this.showPermalinkView(post.id, false);
this.props.actions.showPermalink(this.context.intl, '', post.id, false);
};
renderEmpty = () => {
@ -185,28 +169,6 @@ export default class PinnedPosts extends PureComponent {
);
};
showPermalinkView = (postId, isPermalink) => {
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
};
retry = () => {
this.getPinnedPosts();
};

View file

@ -13,10 +13,9 @@ describe('PinnedPosts', () => {
const baseProps = {
actions: {
clearSearch: jest.fn(),
loadChannelsByTeamName: jest.fn(),
getPostThread: jest.fn(),
getPinnedPosts: jest.fn(),
selectFocusedPostId: jest.fn(),
showPermalink: jest.fn(),
selectPost: jest.fn(),
},
theme: Preferences.THEMES.default,

View file

@ -4,9 +4,9 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {loadChannelsByTeamName} from '@actions/views/channel';
import {showPermalink} from '@actions/views/permalink';
import {getPostThread} from '@actions/views/post';
import {selectFocusedPostId, selectPost} from '@mm-redux/actions/posts';
import {selectPost} from '@mm-redux/actions/posts';
import {clearSearch, getRecentMentions} from '@mm-redux/actions/search';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {makePreparePostIdsForSearchPosts} from '@selectors/post_list';
@ -29,11 +29,10 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
clearSearch,
loadChannelsByTeamName,
getPostThread,
getRecentMentions,
selectFocusedPostId,
selectPost,
showPermalink,
}, dispatch),
};
}

View file

@ -12,34 +12,26 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {dismissModal, goToScreen, showSearchModal} from '@actions/navigation';
import ChannelLoader from '@components/channel_loader';
import DateHeader from '@components/post_list/date_header';
import FailedNetworkAction from '@components/failed_network_action';
import NoResults from '@components/no_results';
import PostSeparator from '@components/post_separator';
import StatusBar from '@components/status_bar';
import {isDateLine, getDateForDateLine} from '@mm-redux/utils/post_list';
import ChannelLoader from 'app/components/channel_loader';
import DateHeader from 'app/components/post_list/date_header';
import FailedNetworkAction from 'app/components/failed_network_action';
import NoResults from 'app/components/no_results';
import PostSeparator from 'app/components/post_separator';
import StatusBar from 'app/components/status_bar';
import SearchResultPost from '@screens/search/search_result_post';
import ChannelDisplayName from '@screens/search/channel_display_name';
import mattermostManaged from 'app/mattermost_managed';
import SearchResultPost from 'app/screens/search/search_result_post';
import ChannelDisplayName from 'app/screens/search/channel_display_name';
import {changeOpacity} from 'app/utils/theme';
import {
goToScreen,
showModalOverCurrentContext,
showSearchModal,
dismissModal,
} from 'app/actions/navigation';
export default class RecentMentions extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
clearSearch: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
getPostThread: PropTypes.func.isRequired,
getRecentMentions: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}).isRequired,
postIds: PropTypes.array,
theme: PropTypes.object.isRequired,
@ -102,15 +94,8 @@ export default class RecentMentions extends PureComponent {
goToScreen(screen, title, passProps);
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handlePermalinkPress = (postId, teamName) => {
this.props.actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId, true);
this.props.actions.showPermalink(this.context.intl, teamName, postId);
};
handleHashtagPress = async (hashtag) => {
@ -127,9 +112,7 @@ export default class RecentMentions extends PureComponent {
}
previewPost = (post) => {
Keyboard.dismiss();
this.showPermalinkView(post.id, false);
this.props.actions.showPermalink(this.context.intl, '', post.id, false);
};
renderEmpty = () => {
@ -185,28 +168,6 @@ export default class RecentMentions extends PureComponent {
);
};
showPermalinkView = (postId, isPermalink) => {
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
};
retry = () => {
this.getRecentMentions();
};

View file

@ -14,10 +14,9 @@ describe('RecentMentions', () => {
const baseProps = {
actions: {
clearSearch: jest.fn(),
loadChannelsByTeamName: jest.fn(),
getPostThread: jest.fn(),
getRecentMentions: jest.fn(),
selectFocusedPostId: jest.fn(),
showPermalink: jest.fn(),
selectPost: jest.fn(),
},
theme: Preferences.THEMES.default,

View file

@ -4,10 +4,10 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {loadChannelsByTeamName} from '@actions/views/channel';
import {closePermalink, showPermalink} from '@actions/views/permalink';
import {getPostThread} from '@actions/views/post';
import {handleSearchDraftChanged} from '@actions/views/search';
import {selectFocusedPostId, selectPost} from '@mm-redux/actions/posts';
import {selectPost} from '@mm-redux/actions/posts';
import {clearSearch, removeSearchTerms, searchPostsWithParams, getMorePostsForSearch} from '@mm-redux/actions/search';
import {getCurrentChannelId, filterPostIds} from '@mm-redux/selectors/entities/channels';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
@ -74,14 +74,14 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
clearSearch,
closePermalink,
handleSearchDraftChanged,
loadChannelsByTeamName,
getPostThread,
removeSearchTerms,
selectFocusedPostId,
searchPostsWithParams,
getMorePostsForSearch,
selectPost,
showPermalink,
}, dispatch),
};
}

View file

@ -17,7 +17,8 @@ import {
import {Navigation} from 'react-native-navigation';
import HWKeyboardEvent from 'react-native-hw-keyboard-event';
import {goToScreen, showModalOverCurrentContext, dismissModal} from '@actions/navigation';
import {goToScreen, dismissModal} from '@actions/navigation';
import {showingPermalink} from '@actions/views/permalink';
import Autocomplete from '@components/autocomplete';
import CompassIcon from '@components/compass_icon';
import KeyboardLayout from '@components/layout/keyboard_layout';
@ -58,14 +59,14 @@ export default class Search extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
clearSearch: PropTypes.func.isRequired,
closePermalink: PropTypes.func.isRequired,
handleSearchDraftChanged: PropTypes.func.isRequired,
loadChannelsByTeamName: PropTypes.func.isRequired,
getPostThread: PropTypes.func.isRequired,
removeSearchTerms: PropTypes.func.isRequired,
searchPostsWithParams: PropTypes.func.isRequired,
getMorePostsForSearch: PropTypes.func.isRequired,
selectFocusedPostId: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired,
showPermalink: PropTypes.func.isRequired,
}).isRequired,
currentTeamId: PropTypes.string.isRequired,
initialValue: PropTypes.string,
@ -235,9 +236,9 @@ export default class Search extends PureComponent {
};
handleHashtagPress = (hashtag) => {
if (this.showingPermalink) {
if (showingPermalink) {
dismissModal();
this.handleClosePermalink();
this.props.actions.closePermalink();
}
const terms = '#' + hashtag;
@ -248,20 +249,13 @@ export default class Search extends PureComponent {
Keyboard.dismiss();
};
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handleLayout = (event) => {
const {height} = event.nativeEvent.layout;
this.setState({searchListHeight: height});
};
handlePermalinkPress = (postId, teamName) => {
this.props.actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId, true);
this.props.actions.showPermalink(this.context.intl, teamName, postId);
};
handleScroll = (event) => {
@ -337,9 +331,7 @@ export default class Search extends PureComponent {
}, 100);
previewPost = (post) => {
Keyboard.dismiss();
this.showPermalinkView(post.id, false);
this.props.actions.showPermalink(this.context.intl, '', post.id, false);
};
removeSearchTerms = preventDoubleTap((item) => {
@ -477,28 +469,6 @@ export default class Search extends PureComponent {
this.search(this.state.value.trim());
};
showPermalinkView = (postId, isPermalink) => {
const {actions} = this.props;
actions.selectFocusedPostId(postId);
if (!this.showingPermalink) {
const screen = 'Permalink';
const passProps = {
isPermalink,
onClose: this.handleClosePermalink,
};
const options = {
layout: {
backgroundColor: changeOpacity('#000', 0.2),
},
};
this.showingPermalink = true;
showModalOverCurrentContext(screen, passProps, options);
}
};
scrollToTop = () => {
if (this.listRef?._wrapperListRef) {
this.listRef._wrapperListRef.getListRef().scrollToOffset({

View file

@ -4,6 +4,7 @@
import {Alert, Platform} from 'react-native';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import {t} from '@utils/i18n';
import {Posts} from '@mm-redux/constants';
const INVALID_VERSIONS = ['1.29.0'];
@ -95,4 +96,13 @@ export function validatePreviousVersion(previousVersion) {
}
return true;
}
}
export function permalinkBadTeam(intl) {
const message = {
id: t('mobile.server_link.unreachable_team.error'),
defaultMessage: 'This link belongs to a deleted team or to a team to which you do not have access.',
};
alertErrorWithFallback(intl, {}, message);
}