diff --git a/app/screens/permalink/__snapshots__/permalink.test.js.snap b/app/screens/permalink/__snapshots__/permalink.test.js.snap
new file mode 100644
index 000000000..5cdb8ba52
--- /dev/null
+++ b/app/screens/permalink/__snapshots__/permalink.test.js.snap
@@ -0,0 +1,152 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Permalink should match snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+ channel_name
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`Permalink should match snapshot 2`] = `
+
+
+
+
+`;
diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js
index 1b73d9691..1accbe827 100644
--- a/app/screens/permalink/permalink.js
+++ b/app/screens/permalink/permalink.js
@@ -86,10 +86,49 @@ export default class Permalink extends PureComponent {
intl: intlShape.isRequired,
};
+ static getDerivedStateFromProps(nextProps, prevState) {
+ const newState = {};
+ if (nextProps.focusedPostId !== prevState.focusedPostIdState) {
+ newState.focusedPostIdState = nextProps.focusedPostId;
+ }
+
+ if (nextProps.channelId && nextProps.channelId !== prevState.channelIdState) {
+ newState.channelIdState = nextProps.channelId;
+ }
+
+ if (nextProps.channelName && nextProps.channelName !== prevState.channelNameState) {
+ newState.channelNameState = nextProps.channelName;
+ }
+
+ if (nextProps.postIds.length > 0 && nextProps.postIds !== prevState.postIdsState) {
+ newState.postIdsState = nextProps.postIds;
+ }
+
+ if (nextProps.focusedPostId !== prevState.focusedPostIdState) {
+ let loading = true;
+ if (nextProps.postIds && nextProps.postIds.length >= 10) {
+ loading = false;
+ }
+
+ newState.loading = loading;
+ }
+
+ if (Object.keys(newState).length === 0) {
+ return null;
+ }
+
+ return newState;
+ }
+
constructor(props) {
super(props);
- const {postIds, channelName} = props;
+ const {
+ postIds,
+ channelId,
+ channelName,
+ focusedPostId,
+ } = props;
let loading = true;
if (postIds && postIds.length >= 10) {
@@ -103,10 +142,14 @@ export default class Permalink extends PureComponent {
loading,
error: '',
retry: false,
+ channelIdState: channelId,
+ channelNameState: channelName,
+ focusedPostIdState: focusedPostId,
+ postIdsState: postIds,
};
}
- componentWillMount() {
+ componentDidMount() {
this.mounted = true;
if (this.state.loading) {
@@ -114,18 +157,9 @@ export default class Permalink extends PureComponent {
}
}
- componentWillReceiveProps(nextProps) {
- if (this.props.channelName !== nextProps.channelName && this.mounted) {
- this.setState({title: nextProps.channelName});
- }
-
- if (this.props.focusedPostId !== nextProps.focusedPostId && this.mounted) {
- this.setState({loading: true});
- if (nextProps.postIds && nextProps.postIds.length < 10) {
- this.loadPosts(nextProps);
- } else {
- this.setState({loading: false});
- }
+ componentDidUpdate() {
+ if (this.state.loading) {
+ this.loadPosts(this.props);
}
}
@@ -176,11 +210,11 @@ export default class Permalink extends PureComponent {
};
handlePress = () => {
- const {channelId, channelName} = this.props;
+ const {channelIdState, channelNameState} = this.state;
if (this.refs.view) {
this.refs.view.growOut().then(() => {
- this.jumpToChannel(channelId, channelName);
+ this.jumpToChannel(channelIdState, channelNameState);
});
}
};
@@ -303,18 +337,21 @@ export default class Permalink extends PureComponent {
}
};
- archivedIcon = (style) => {
- let ico = null;
+ archivedIcon = () => {
+ const style = getStyleSheet(this.props.theme);
+ let icon = null;
if (this.props.channelIsArchived) {
- ico = (
-
- {' '}
- );
+ icon = (
+
+
+ {' '}
+
+ );
}
- return ico;
+ return icon;
};
render() {
@@ -324,10 +361,15 @@ export default class Permalink extends PureComponent {
navigator,
onHashtagPress,
onPermalinkPress,
- postIds,
theme,
} = this.props;
- const {error, retry, loading, title} = this.state;
+ const {
+ error,
+ retry,
+ loading,
+ postIdsState,
+ title,
+ } = this.state;
const style = getStyleSheet(theme);
let postList;
@@ -359,7 +401,7 @@ export default class Permalink extends PureComponent {
onHashtagPress={onHashtagPress}
onPermalinkPress={onPermalinkPress}
onPostPress={this.goToThread}
- postIds={postIds}
+ postIds={postIdsState}
currentUserId={currentUserId}
lastViewedAt={0}
navigator={navigator}
@@ -404,7 +446,7 @@ export default class Permalink extends PureComponent {
numberOfLines={1}
style={style.title}
>
- {this.archivedIcon(style)}
+ {this.archivedIcon()}
{title}
diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js
new file mode 100644
index 000000000..4f7ad2657
--- /dev/null
+++ b/app/screens/permalink/permalink.test.js
@@ -0,0 +1,132 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {shallow} from 'enzyme';
+
+import Preferences from 'mattermost-redux/constants/preferences';
+
+import Permalink from './permalink.js';
+
+jest.mock('react-intl');
+
+describe('Permalink', () => {
+ const navigator = {
+ dismissAllModals: jest.fn(),
+ dismissModal: jest.fn(),
+ push: jest.fn(),
+ resetTo: jest.fn(),
+ setOnNavigatorEvent: jest.fn(),
+ };
+
+ const actions = {
+ getPostsAfter: jest.fn(),
+ getPostsBefore: jest.fn(),
+ getPostThread: jest.fn(),
+ getChannel: jest.fn(),
+ handleSelectChannel: jest.fn(),
+ handleTeamChange: jest.fn(),
+ joinChannel: jest.fn(),
+ loadThreadIfNecessary: jest.fn(),
+ markChannelAsRead: jest.fn(),
+ markChannelAsViewed: jest.fn(),
+ selectPost: jest.fn(),
+ setChannelDisplayName: jest.fn(),
+ setChannelLoading: jest.fn(),
+ };
+
+ const baseProps = {
+ actions,
+ channelId: 'channel_id',
+ channelIsArchived: false,
+ channelName: 'channel_name',
+ channelTeamId: 'team_id',
+ currentTeamId: 'current_team_id',
+ currentUserId: 'current_user_id',
+ focusedPostId: 'focused_post_id',
+ isPermalink: true,
+ 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,
+ };
+
+ test('should match snapshot', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ expect(wrapper.getElement()).toMatchSnapshot();
+
+ // match archived icon
+ wrapper.setProps({channelIsArchived: true});
+ expect(wrapper.instance().archivedIcon()).toMatchSnapshot();
+ });
+
+ test('should match state and call loadPosts on retry', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ wrapper.instance().loadPosts = jest.fn();
+ wrapper.instance().retry();
+ expect(wrapper.instance().loadPosts).toHaveBeenCalledTimes(2);
+ expect(wrapper.instance().loadPosts).toBeCalledWith(baseProps);
+ });
+
+ test('should call handleClose on onNavigatorEvent(backPress)', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ wrapper.instance().handleClose = jest.fn();
+ wrapper.instance().onNavigatorEvent({id: 'backPress'});
+ expect(wrapper.instance().handleClose).toHaveBeenCalledTimes(1);
+ });
+
+ test('should match state', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ expect(wrapper.state('channelIdState')).toEqual(baseProps.channelId);
+ expect(wrapper.state('channelNameState')).toEqual(baseProps.channelName);
+ expect(wrapper.state('focusedPostIdState')).toEqual(baseProps.focusedPostId);
+ expect(wrapper.state('postIdsState')).toEqual(baseProps.postIds);
+
+ wrapper.setProps({channelId: ''});
+ expect(wrapper.state('channelIdState')).toEqual(baseProps.channelId);
+ wrapper.setProps({channelId: null});
+ expect(wrapper.state('channelIdState')).toEqual(baseProps.channelId);
+ wrapper.setProps({channelId: 'new_channel_id'});
+ expect(wrapper.state('channelIdState')).toEqual('new_channel_id');
+
+ wrapper.setProps({channelName: ''});
+ expect(wrapper.state('channelNameState')).toEqual(baseProps.channelName);
+ wrapper.setProps({channelName: null});
+ expect(wrapper.state('channelNameState')).toEqual(baseProps.channelName);
+ wrapper.setProps({channelName: 'new_channel_name'});
+ expect(wrapper.state('channelNameState')).toEqual('new_channel_name');
+
+ wrapper.setProps({focusedPostId: 'new_focused_post_id'});
+ expect(wrapper.state('focusedPostIdState')).toEqual('new_focused_post_id');
+
+ wrapper.setProps({postIds: []});
+ expect(wrapper.state('postIdsState')).toEqual(baseProps.postIds);
+ wrapper.setProps({postIds: ['post_id_1', 'focused_post_id']});
+ expect(wrapper.state('postIdsState')).toEqual(['post_id_1', 'focused_post_id']);
+
+ wrapper.setProps({postIds: baseProps.postIds, focusedPostId: baseProps.focusedPostId});
+ expect(wrapper.state('loading')).toEqual(true);
+ wrapper.setProps({postIds: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'], focusedPostId: 'new_focused_post_id'});
+ expect(wrapper.state('loading')).toEqual(false);
+ });
+});
diff --git a/app/selectors/post_list.js b/app/selectors/post_list.js
index ab738a329..1cb608043 100644
--- a/app/selectors/post_list.js
+++ b/app/selectors/post_list.js
@@ -44,7 +44,10 @@ export function makePreparePostIdsForPostList() {
for (let i = posts.length - 1; i >= 0; i--) {
const post = posts[i];
- if (post.type === Posts.POST_TYPES.EPHEMERAL_ADD_TO_CHANNEL && !selectedPostId) {
+ if (
+ !post ||
+ (post.type === Posts.POST_TYPES.EPHEMERAL_ADD_TO_CHANNEL && !selectedPostId)
+ ) {
continue;
}