forked from Ivasoft/mattermost-mobile
* Thread permalink fix
* Reverted and implemented solution by updating the props
* fix TS on selectors/entities/posts.ts
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
(cherry picked from commit b3283ec9cf)
Co-authored-by: Anurag Shivarathri <anurag6713@gmail.com>
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
|
||||
import {intlShape} from 'react-intl';
|
||||
import {Keyboard} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import {dismissAllModals, showModalOverCurrentContext} from '@actions/navigation';
|
||||
import {showModalOverCurrentContext} from '@actions/navigation';
|
||||
import {loadChannelsByTeamName} from '@actions/views/channel';
|
||||
import {selectFocusedPostId} from '@mm-redux/actions/posts';
|
||||
import {getPost as fetchPost, selectFocusedPostId} from '@mm-redux/actions/posts';
|
||||
import {getPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
|
||||
import {permalinkBadTeam} from '@utils/general';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
@@ -17,26 +20,50 @@ let showingPermalink = false;
|
||||
|
||||
export function showPermalink(intl: typeof intlShape, teamName: string, postId: string, openAsPermalink = true) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
|
||||
let name = teamName;
|
||||
if (!name) {
|
||||
name = getCurrentTeam(getState()).name;
|
||||
name = getCurrentTeam(state).name;
|
||||
}
|
||||
|
||||
const loadTeam = await dispatch(loadChannelsByTeamName(name, permalinkBadTeam.bind(null, intl)));
|
||||
|
||||
let isThreadPost;
|
||||
|
||||
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
|
||||
if (collapsedThreadsEnabled) {
|
||||
let post = getPost(state, postId);
|
||||
if (!post) {
|
||||
const {data} = await dispatch(fetchPost(postId));
|
||||
if (data) {
|
||||
post = data;
|
||||
}
|
||||
}
|
||||
if (post) {
|
||||
isThreadPost = Boolean(post.root_id);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadTeam.error) {
|
||||
Keyboard.dismiss();
|
||||
dispatch(selectFocusedPostId(postId));
|
||||
if (showingPermalink) {
|
||||
await dismissAllModals();
|
||||
}
|
||||
|
||||
const screen = 'Permalink';
|
||||
const passProps = {
|
||||
isPermalink: openAsPermalink,
|
||||
isThreadPost,
|
||||
focusedPostId: postId,
|
||||
teamName,
|
||||
};
|
||||
|
||||
if (showingPermalink) {
|
||||
Navigation.updateProps(screen, passProps);
|
||||
return {};
|
||||
}
|
||||
|
||||
const options = {
|
||||
layout: {
|
||||
componentBackgroundColor: changeOpacity('#000', 0.2),
|
||||
|
||||
@@ -142,7 +142,16 @@ export function handleThreadArrived(threadData: UserThread, teamId: string) {
|
||||
},
|
||||
{
|
||||
type: PostTypes.RECEIVED_POSTS,
|
||||
data: {posts: [{...thread.post, participants: thread.participants}]},
|
||||
data: {posts: [{
|
||||
|
||||
// Merge post data as thread.post might not contain all post data like "metadata"
|
||||
...(state.entities.posts.posts[thread.id] || {}),
|
||||
|
||||
...thread.post,
|
||||
reply_count: thread.reply_count,
|
||||
last_reply_at: thread.last_reply_at,
|
||||
participants: thread.participants,
|
||||
}]},
|
||||
},
|
||||
{
|
||||
type: ThreadTypes.RECEIVED_THREAD,
|
||||
|
||||
@@ -78,6 +78,7 @@ export const getPostsInCurrentChannel: (a: GlobalState) => PostWithFormatData[]
|
||||
const getPostsInChannel = makeGetPostsInChannel();
|
||||
return (state: GlobalState) => getPostsInChannel(state, state.entities.channels.currentChannelId, -1);
|
||||
})();
|
||||
|
||||
export function makeGetPostIdsForThread(): (b: GlobalState, a: $ID<Post>) => Array<$ID<Post>> {
|
||||
return createIdsSelector(
|
||||
getAllPosts,
|
||||
@@ -101,6 +102,25 @@ export function makeGetPostIdsForThread(): (b: GlobalState, a: $ID<Post>) => Arr
|
||||
);
|
||||
}
|
||||
|
||||
export function makeGetPostIdsForThreadWithLimit(): (b: GlobalState, a: $ID<Post>, c: string, d: number, e: number) => Array<$ID<Post>> {
|
||||
const getPostIdsForThread = makeGetPostIdsForThread();
|
||||
return createIdsSelector(
|
||||
(state: GlobalState, rootId: string) => getPostIdsForThread(state, rootId),
|
||||
(state: GlobalState, rootId: string, focusedPostId: string) => focusedPostId,
|
||||
(state: GlobalState, rootId: string, focusedPostId: string, postsBeforeCount: number) => postsBeforeCount,
|
||||
(state: GlobalState, rootId: string, focusedPostId: string, postsBeforeCount: number, postsAfterCount: number) => postsAfterCount,
|
||||
(postIds: Array<$ID<Post>>, focusedPostId: string, postsBeforeCount = Posts.POST_CHUNK_SIZE / 2, postsAfterCount = Posts.POST_CHUNK_SIZE / 2) => {
|
||||
const index = postIds.indexOf(focusedPostId);
|
||||
if (index > -1) {
|
||||
const minPostIndex = Math.max(index - postsAfterCount, 0);
|
||||
const maxPostIndex = Math.min(index + postsBeforeCount + 1, postIds.length);
|
||||
return postIds.slice(minPostIndex, maxPostIndex);
|
||||
}
|
||||
return postIds;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function makeGetPostsChunkAroundPost(): (c: GlobalState, b: $ID<Post>, a: $ID<Channel>) => PostOrderBlock| null | undefined {
|
||||
return createIdsSelector(
|
||||
(state: GlobalState, postId: string, channelId: string) => state.entities.posts.postsInChannel[channelId],
|
||||
|
||||
@@ -12,7 +12,7 @@ import {getChannel as getChannelAction, joinChannel} from '@mm-redux/actions/cha
|
||||
import {selectPost} from '@mm-redux/actions/posts';
|
||||
import {addUserToTeam, getTeamByName, removeUserFromTeam} from '@mm-redux/actions/teams';
|
||||
import {makeGetChannel, getMyChannelMemberships} from '@mm-redux/selectors/entities/channels';
|
||||
import {makeGetPostIdsAroundPost, getPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {makeGetPostIdsAroundPost, getPost, makeGetPostIdsForThreadWithLimit} from '@mm-redux/selectors/entities/posts';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeamId, getTeamByName as selectTeamByName, getTeamMemberships} from '@mm-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
|
||||
@@ -20,22 +20,31 @@ import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
|
||||
import Permalink from './permalink';
|
||||
|
||||
function makeMapStateToProps() {
|
||||
const getPostIdsForThread = makeGetPostIdsForThreadWithLimit();
|
||||
const getPostIdsAroundPost = makeGetPostIdsAroundPost();
|
||||
const getChannel = makeGetChannel();
|
||||
|
||||
return function mapStateToProps(state, props) {
|
||||
const {currentFocusedPostId} = state.entities.posts;
|
||||
const post = getPost(state, currentFocusedPostId);
|
||||
const {focusedPostId} = props;
|
||||
const post = getPost(state, focusedPostId);
|
||||
|
||||
let channel;
|
||||
let postIds;
|
||||
|
||||
if (post) {
|
||||
channel = getChannel(state, {id: post.channel_id});
|
||||
postIds = getPostIdsAroundPost(state, currentFocusedPostId, post.channel_id, {
|
||||
|
||||
const options = {
|
||||
postsBeforeCount: 10,
|
||||
postsAfterCount: 10,
|
||||
});
|
||||
};
|
||||
|
||||
// It is passed only when CRT is enabled and post has a root_id
|
||||
if (props.isThreadPost) {
|
||||
postIds = getPostIdsForThread(state, post.root_id, focusedPostId, options.postsBeforeCount, options.postsAfterCount);
|
||||
} else {
|
||||
postIds = getPostIdsAroundPost(state, focusedPostId, post.channel_id, options);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -45,9 +54,10 @@ function makeMapStateToProps() {
|
||||
channelTeamId: channel ? channel.team_id : '',
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUserId: getCurrentUserId(state),
|
||||
focusedPostId: currentFocusedPostId,
|
||||
focusedPostId,
|
||||
myChannelMemberships: getMyChannelMemberships(state),
|
||||
myTeamMemberships: getTeamMemberships(state),
|
||||
post,
|
||||
postIds,
|
||||
team: selectTeamByName(state, props.teamName),
|
||||
theme: getTheme(state),
|
||||
|
||||
@@ -66,8 +66,10 @@ export default class Permalink extends PureComponent {
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
focusedPostId: PropTypes.string.isRequired,
|
||||
isPermalink: PropTypes.bool,
|
||||
isThreadPost: PropTypes.bool,
|
||||
myChannelMemberships: PropTypes.object.isRequired,
|
||||
myTeamMemberships: PropTypes.object.isRequired,
|
||||
post: PropTypes.object,
|
||||
postIds: PropTypes.array,
|
||||
team: PropTypes.object,
|
||||
teamName: PropTypes.string,
|
||||
@@ -106,6 +108,7 @@ export default class Permalink extends PureComponent {
|
||||
this.navigationEventListener = Navigation.events().bindComponent(this);
|
||||
|
||||
this.mounted = true;
|
||||
this.focusedPostId = this.props.focusedPostId;
|
||||
|
||||
if (this.state.loading && this.props.focusedPostId) {
|
||||
this.initialLoad = true;
|
||||
@@ -114,7 +117,13 @@ export default class Permalink extends PureComponent {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.loading && this.props.focusedPostId && !this.initialLoad) {
|
||||
let focusedPostIdChanged;
|
||||
if (this.focusedPostId !== this.props.focusedPostId) {
|
||||
focusedPostIdChanged = true;
|
||||
this.focusedPostId = this.props.focusedPostId;
|
||||
}
|
||||
|
||||
if (focusedPostIdChanged || (this.state.loading && this.props.focusedPostId && !this.initialLoad)) {
|
||||
this.loadPosts();
|
||||
}
|
||||
}
|
||||
@@ -155,7 +164,7 @@ export default class Permalink extends PureComponent {
|
||||
|
||||
jumpToChannel = async (channelId) => {
|
||||
if (channelId) {
|
||||
const {actions, channelId: currentChannelId, channelTeamId, currentTeamId} = this.props;
|
||||
const {actions, channelId: currentChannelId, channelTeamId, currentTeamId, isThreadPost, post} = this.props;
|
||||
const {closePermalink, handleSelectChannel, handleTeamChange} = actions;
|
||||
|
||||
actions.selectPost('');
|
||||
@@ -178,13 +187,20 @@ export default class Permalink extends PureComponent {
|
||||
handleTeamChange(channelTeamId);
|
||||
}
|
||||
|
||||
handleSelectChannel(channelId);
|
||||
await handleSelectChannel(channelId);
|
||||
|
||||
if (isThreadPost) {
|
||||
EventEmitter.emit('goToThread', {
|
||||
id: post?.root_id,
|
||||
channel_id: channelId,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadPosts = async () => {
|
||||
const {intl} = this.context;
|
||||
const {actions, channelId, currentUserId, focusedPostId, isPermalink, postIds} = this.props;
|
||||
const {actions, channelId, currentUserId, focusedPostId, isPermalink, isThreadPost, postIds} = this.props;
|
||||
const {formatMessage} = intl;
|
||||
let focusChannelId = channelId;
|
||||
|
||||
@@ -273,7 +289,9 @@ export default class Permalink extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
await actions.getPostsAround(focusChannelId, focusedPostId, 10);
|
||||
if (!isThreadPost) {
|
||||
await actions.getPostsAround(focusChannelId, focusedPostId, 10);
|
||||
}
|
||||
|
||||
if (this.initialLoad) {
|
||||
this.initialLoad = false;
|
||||
@@ -307,7 +325,7 @@ export default class Permalink extends PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {channelName, currentUserId, focusedPostId, postIds, theme} = this.props;
|
||||
const {channelName, currentUserId, focusedPostId, isThreadPost, postIds, theme} = this.props;
|
||||
const {error, joinChannelPromptVisible, loading, retry, title} = this.state;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
@@ -383,7 +401,27 @@ export default class Permalink extends PureComponent {
|
||||
style={style.title}
|
||||
>
|
||||
{this.archivedIcon()}
|
||||
{title || channelName}
|
||||
|
||||
{title || (isThreadPost ? (
|
||||
<FormattedText
|
||||
id='mobile.routes.thread_crt'
|
||||
defautMessage='Thread'
|
||||
/>
|
||||
) : channelName)}
|
||||
|
||||
{isThreadPost && (
|
||||
<>
|
||||
{' '}
|
||||
<FormattedText
|
||||
id='mobile.routes.thread_crt.in'
|
||||
defaultMessage='in {channelName}'
|
||||
style={style.description}
|
||||
values={{
|
||||
channelName,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -458,6 +496,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
postList: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
@@ -26,8 +26,8 @@ function makeMapStateToProps() {
|
||||
const myMember = getMyCurrentChannelMembership(state);
|
||||
const thread = collapsedThreadsEnabled ? getThread(state, ownProps.rootId, true) : null;
|
||||
let lastViewedAt = myMember?.last_viewed_at;
|
||||
if (collapsedThreadsEnabled && thread) {
|
||||
lastViewedAt = getThreadLastViewedAt(state, thread.id);
|
||||
if (collapsedThreadsEnabled) {
|
||||
lastViewedAt = getThreadLastViewedAt(state, thread?.id);
|
||||
}
|
||||
return {
|
||||
channelId: ownProps.channelId,
|
||||
|
||||
Reference in New Issue
Block a user