MM-37485 MM-37602 MM-37610 MM-37684 MM-37719 MM-37522 MM-37720 App crash, Sync issue fixes (#5612)
Summary Fixes app crash on opening channel header Fixes app crash on opening permalink from permalink screen Fixes showing duplicates channels while searching in "Jump to..." Fixes auto following the thread on opening the thread view when there are unreads Fixes updation of threads state (Read/Unread/Deleted) of already loaded threads Fixes counter showing as blank due to threads sync issue Doesn't show Follow Thread/Message when CRT is not enabled Ticket Link https://mattermost.atlassian.net/browse/MM-37485 https://mattermost.atlassian.net/browse/MM-37602 https://mattermost.atlassian.net/browse/MM-37610 https://mattermost.atlassian.net/browse/MM-37684 https://mattermost.atlassian.net/browse/MM-37719 https://mattermost.atlassian.net/browse/MM-37522 https://mattermost.atlassian.net/browse/MM-37720 Device Information This PR was tested on: iOS 14.5
This commit is contained in:
parent
e280400994
commit
fc929891ea
10 changed files with 57 additions and 31 deletions
|
|
@ -54,8 +54,8 @@ function GlobalThreadsList({actions, allThreadIds, intl, teamId, theme, threadCo
|
|||
React.useEffect(() => {
|
||||
// Loads on mount, Loads on team change
|
||||
scrollToTop();
|
||||
loadThreads('', ids[0]);
|
||||
}, [teamId]);
|
||||
loadThreads('', '', viewingUnreads);
|
||||
}, [teamId, viewingUnreads]);
|
||||
|
||||
// Prevent from being called when an active request is pending.
|
||||
const loadMoreThreads = async () => {
|
||||
|
|
@ -79,18 +79,6 @@ function GlobalThreadsList({actions, allThreadIds, intl, teamId, theme, threadCo
|
|||
}
|
||||
};
|
||||
|
||||
const handleViewAllThreads = () => {
|
||||
scrollToTop();
|
||||
loadThreads('', allThreadIds[0], false);
|
||||
actions.handleViewingGlobalThreadsAll();
|
||||
};
|
||||
|
||||
const handleViewUnreadThreads = () => {
|
||||
scrollToTop();
|
||||
loadThreads('', unreadThreadIds[0], true);
|
||||
actions.handleViewingGlobalThreadsUnreads();
|
||||
};
|
||||
|
||||
const markAllAsRead = () => {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
|
|
@ -131,8 +119,8 @@ function GlobalThreadsList({actions, allThreadIds, intl, teamId, theme, threadCo
|
|||
theme={theme}
|
||||
threadIds={ids}
|
||||
viewingUnreads={viewingUnreads}
|
||||
viewAllThreads={handleViewAllThreads}
|
||||
viewUnreadThreads={handleViewUnreadThreads}
|
||||
viewAllThreads={actions.handleViewingGlobalThreadsAll}
|
||||
viewUnreadThreads={actions.handleViewingGlobalThreadsUnreads}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ exports[`ChannelsList List should match snapshot with collapsed threads enabled
|
|||
Array [
|
||||
undefined,
|
||||
Object {
|
||||
"marginTop": 70,
|
||||
"marginTop": 64,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ export default class List extends PureComponent {
|
|||
const paddingBottom = this.listContentPadding();
|
||||
const indicatorStyle = [styles.above];
|
||||
if (collapsedThreadsEnabled) {
|
||||
indicatorStyle.push({marginTop: 70});
|
||||
indicatorStyle.push({marginTop: 64});
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ export function getThreads(userId: string, teamId: string, before = '', after =
|
|||
}
|
||||
|
||||
if (userThreadList) {
|
||||
const currentUserId = getCurrentUserId(getState());
|
||||
|
||||
const data = {
|
||||
threads: [] as UserThread[],
|
||||
participants: [] as UserThread['participants'],
|
||||
|
|
@ -43,10 +45,15 @@ export function getThreads(userId: string, teamId: string, before = '', after =
|
|||
is_following: true,
|
||||
});
|
||||
|
||||
// participants, participantIds
|
||||
// data.participantIds - Get Missing Profiles
|
||||
// data.participants - Received Profile List
|
||||
thread.participants?.forEach((participant) => {
|
||||
data.participantIds.push(participant.id);
|
||||
data.participants.push(participant);
|
||||
|
||||
// Exclude current user
|
||||
if (participant.id !== currentUserId) {
|
||||
data.participants.push(participant);
|
||||
}
|
||||
});
|
||||
|
||||
// posts
|
||||
|
|
@ -63,6 +70,11 @@ export function getThreads(userId: string, teamId: string, before = '', after =
|
|||
...userThreadList,
|
||||
threads: data.threads,
|
||||
team_id: teamId,
|
||||
removeOldThreads:
|
||||
!before && !after && // When loading on mount
|
||||
perPage >= ViewTypes.CRT_CHUNK_SIZE && // Exclude on load where we get 5 threads
|
||||
!since && // Exclude reconnect
|
||||
!unread, // Exclude when user is loading unreads or on switching to "All your threads" it will take time to fetch from server
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@ export const threadsReducer = (state: ThreadsState['threads'] = {}, action: Gene
|
|||
export const threadsInTeamReducer = (state: ThreadsState['threadsInTeam'] = {}, action: GenericAction) => {
|
||||
switch (action.type) {
|
||||
case ThreadTypes.RECEIVED_THREADS: {
|
||||
const nextSet = new Set(state[action.data.team_id]);
|
||||
const threads = action.data.removeOldThreads ? [] : state[action.data.team_id];
|
||||
const nextSet = new Set(threads);
|
||||
|
||||
action.data.threads.forEach((thread: UserThread) => {
|
||||
nextSet.add(thread.id);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@ export function buildDisplayableChannelListWithUnreadSection(usersState: UsersSt
|
|||
const missingDirectChannels = createMissingDirectChannels(currentUserId, myChannels, myPreferences);
|
||||
const channels = buildChannels(usersState, myChannels, missingDirectChannels, teammateNameDisplay, locale);
|
||||
const unreadChannels = [...buildChannelsWithMentions(channels, myMembers, locale), ...buildUnreadChannels(channels, myMembers, locale)];
|
||||
const notUnreadChannels = channels.filter((channel: Channel) => !isUnreadChannel(myMembers, channel, collapsedThreadsEnabled));
|
||||
|
||||
// collapsedThreadsEnabled is set to "false" as we are filtering not just based on root posts
|
||||
const notUnreadChannels = channels.filter((channel: Channel) => !isUnreadChannel(myMembers, channel, false));
|
||||
|
||||
const favoriteChannels = buildFavoriteChannels(notUnreadChannels, myPreferences, locale);
|
||||
const notFavoriteChannels = buildNotFavoriteChannels(notUnreadChannels, myPreferences);
|
||||
const directAndGroupChannels = buildDirectAndGroupChannels(notFavoriteChannels, myMembers, config, myPreferences, currentUserId, profiles, lastPosts, collapsedThreadsEnabled);
|
||||
|
|
|
|||
|
|
@ -112,13 +112,13 @@ export default class PostOptions extends PureComponent {
|
|||
|
||||
getFollowThreadOption = () => {
|
||||
const {location, thread} = this.props;
|
||||
if (location !== CHANNEL) {
|
||||
if (location !== CHANNEL || !thread) {
|
||||
return null;
|
||||
}
|
||||
const key = 'follow';
|
||||
let icon;
|
||||
let message;
|
||||
if (thread?.is_following) {
|
||||
if (thread.is_following) {
|
||||
icon = 'message-minus-outline';
|
||||
if (thread?.participants?.length) {
|
||||
message = {id: t('threads.unfollowThread'), defaultMessage: 'Unfollow Thread'};
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ export default class ThreadBase extends PureComponent {
|
|||
const options = {};
|
||||
|
||||
if (props.collapsedThreadsEnabled) {
|
||||
// Without unique id, it breaks navigation from permalink view.
|
||||
this.threadFollowId = Math.floor(Math.random() * 0x10000000000).toString(16);
|
||||
|
||||
let titleText;
|
||||
if (channelType === General.DM_CHANNEL) {
|
||||
titleText = formatMessage({id: 'mobile.routes.thread_dm', defaultMessage: 'Direct Message Thread'});
|
||||
|
|
@ -67,9 +70,9 @@ export default class ThreadBase extends PureComponent {
|
|||
},
|
||||
rightButtons: [
|
||||
{
|
||||
id: '1',
|
||||
id: 1,
|
||||
component: {
|
||||
id: 'ThreadFollow',
|
||||
id: this.threadFollowId,
|
||||
name: 'ThreadFollow',
|
||||
passProps: {
|
||||
active: thread?.is_following,
|
||||
|
|
@ -129,7 +132,7 @@ export default class ThreadBase extends PureComponent {
|
|||
}
|
||||
|
||||
if (this.props.thread?.is_following !== nextProps.thread?.is_following) {
|
||||
Navigation.updateProps('ThreadFollow', {active: nextProps.thread?.is_following});
|
||||
Navigation.updateProps(this.threadFollowId, {active: nextProps.thread?.is_following});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +151,7 @@ export default class ThreadBase extends PureComponent {
|
|||
if (
|
||||
this.props.collapsedThreadsEnabled &&
|
||||
this.props.thread &&
|
||||
this.props.thread.is_following &&
|
||||
(
|
||||
hasNewPost ||
|
||||
this.props.thread.last_viewed_at < this.props.thread.last_reply_at ||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Posts} from '@mm-redux/constants';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
export function getLastChannelForTeam(payload) {
|
||||
|
|
@ -104,7 +105,7 @@ export function cleanUpState(payload, keepCurrent = false) {
|
|||
// Keep the last 60 threads in each team
|
||||
nextEntities.threads = {
|
||||
...nextEntities.threads,
|
||||
...cleanUpThreadsInTeam(payload.entities.threads?.threads, payload.entities.threads?.threadsInTeam, keepCurrent ? lastTeamId : ''),
|
||||
...cleanUpThreadsInTeam(payload.entities.posts?.posts, payload.entities.threads?.threads, payload.entities.threads?.threadsInTeam, keepCurrent ? lastTeamId : ''),
|
||||
};
|
||||
postIdsToKeep.push(...getAllFromThreadsInTeam(nextEntities.threads?.threadsInTeam));
|
||||
|
||||
|
|
@ -274,7 +275,7 @@ export function cleanUpPostsInChannel(postsInChannel, lastChannelForTeam, curren
|
|||
return nextPostsInChannel;
|
||||
}
|
||||
|
||||
export function cleanUpThreadsInTeam(threads, threadsInTeam, currentTeamId, threadsCountPerTeam = 60) {
|
||||
export function cleanUpThreadsInTeam(posts, threads, threadsInTeam, currentTeamId, threadsCountPerTeam = 60) {
|
||||
const newThreads = {};
|
||||
const newThreadsInTeam = {};
|
||||
if (threads && threadsInTeam) {
|
||||
|
|
@ -292,7 +293,8 @@ export function cleanUpThreadsInTeam(threads, threadsInTeam, currentTeamId, thre
|
|||
newThreadsInTeam[teamId] = [];
|
||||
const retainedThreads = currentTeamId === teamId ? mappedThreads : mappedThreads.slice(0, threadsCountPerTeam);
|
||||
retainedThreads.forEach((thread) => {
|
||||
if (thread) {
|
||||
const post = posts?.[thread.id];
|
||||
if (post && post.state !== Posts.POST_DELETED) {
|
||||
newThreadsInTeam[teamId].push(thread.id);
|
||||
newThreads[thread.id] = thread;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import DeviceInfo from 'react-native-device-info';
|
|||
import {REHYDRATE} from 'redux-persist';
|
||||
|
||||
import {ViewTypes} from '@constants';
|
||||
import {Posts} from '@mm-redux/constants';
|
||||
import initialState from '@store/initial_state';
|
||||
|
||||
import {
|
||||
|
|
@ -734,6 +735,16 @@ describe('cleanUpThreadsInTeam', () => {
|
|||
team1: ['thread1', 'thread2', 'thread3'],
|
||||
team2: ['thread3', 'thread4', 'thread5'],
|
||||
team3: ['thread1', 'thread2', 'thread3', 'thread4'],
|
||||
team4: ['thread7'],
|
||||
};
|
||||
const posts = {
|
||||
thread1: {},
|
||||
thread2: {},
|
||||
thread3: {},
|
||||
thread4: {},
|
||||
thread5: {},
|
||||
thread6: {},
|
||||
thread7: {state: Posts.POST_DELETED},
|
||||
};
|
||||
const threads = {
|
||||
thread1: {id: 'thread1', last_reply_at: 100},
|
||||
|
|
@ -742,9 +753,10 @@ describe('cleanUpThreadsInTeam', () => {
|
|||
thread4: {id: 'thread4', last_reply_at: 97},
|
||||
thread5: {id: 'thread5', last_reply_at: 96},
|
||||
thread6: {id: 'thread6', last_reply_at: 95},
|
||||
thread7: {id: 'thread7', last_reply_at: 100},
|
||||
};
|
||||
|
||||
const {threads: nextThreads, threadsInTeam: nextThreadsInTeam} = cleanUpThreadsInTeam(threads, threadsInTeam, 'team3', 2);
|
||||
const {threads: nextThreads, threadsInTeam: nextThreadsInTeam} = cleanUpThreadsInTeam(posts, threads, threadsInTeam, 'team3', 2);
|
||||
|
||||
test('should only keep limited threads per team', () => {
|
||||
expect(nextThreadsInTeam.team1).toEqual(['thread1', 'thread2']);
|
||||
|
|
@ -762,6 +774,10 @@ describe('cleanUpThreadsInTeam', () => {
|
|||
test('Should exclude passed teamId', () => {
|
||||
expect(nextThreadsInTeam.team3).toEqual(threadsInTeam.team3);
|
||||
});
|
||||
|
||||
test('Should not include deleted posts', () => {
|
||||
expect(nextThreadsInTeam.team4).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllFromPostsInChannel', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue