[Gekidou][MM-43870, MM-43896, MM-43898, MM-43902, MM-43903] CRT related bug fixes (#6205)

* Fixes

* Updated snapshot

* Excluding current user from getting updated

* Doesn't handle users if it's empty

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Anurag Shivarathri 2022-05-05 18:36:18 +05:30 committed by GitHub
parent 251ef0992b
commit 2deb2e01e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 119 additions and 23 deletions

View file

@ -8,7 +8,7 @@ import DatabaseManager from '@database/manager';
import {getTranslations, t} from '@i18n';
import {getChannelById} from '@queries/servers/channel';
import {getPostById} from '@queries/servers/post';
import {getCurrentTeamId, setCurrentChannelId} from '@queries/servers/system';
import {getCurrentTeamId, getCurrentUserId, setCurrentChannelId} from '@queries/servers/system';
import {addChannelToTeamHistory} from '@queries/servers/team';
import {getIsCRTEnabled, getThreadById, prepareThreadsFromReceivedPosts, queryThreadsInTeam} from '@queries/servers/thread';
import {getCurrentUser} from '@queries/servers/user';
@ -176,6 +176,9 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
return {error: `${serverUrl} database not found`};
}
const {database} = operator;
const currentUserId = await getCurrentUserId(database);
const posts: Post[] = [];
const users: UserProfile[] = [];
@ -183,7 +186,11 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
for (let i = 0; i < threads.length; i++) {
const {participants, post} = threads[i];
posts.push(post);
participants.forEach((participant) => users.push(participant));
participants.forEach((participant) => {
if (currentUserId !== participant.id) {
users.push(participant);
}
});
}
const postModels = await operator.handlePosts({
@ -200,12 +207,15 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
loadedInGlobalThreads,
});
const userModels = await operator.handleUsers({
users,
prepareRecordsOnly: true,
});
const models = [...postModels, ...threadModels];
const models = [...postModels, ...threadModels, ...userModels];
if (users.length) {
const userModels = await operator.handleUsers({
users,
prepareRecordsOnly: true,
});
models.push(...userModels);
}
if (!prepareRecordsOnly) {
await operator.batchRecords(models);

View file

@ -179,6 +179,38 @@ export const updateThreadRead = async (serverUrl: string, teamId: string, thread
}
};
export const markThreadAsUnread = async (serverUrl: string, teamId: string, threadId: string, postId: string) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return {error: `${serverUrl} database not found`};
}
let client;
try {
client = NetworkManager.getClient(serverUrl);
} catch (error) {
return {error};
}
try {
const data = await client.markThreadAsUnread('me', teamId, threadId, postId);
// Update locally
const post = await getPostById(database, threadId);
if (post) {
await updateThread(serverUrl, threadId, {
last_viewed_at: post.createAt - 1,
});
}
return {data};
} catch (error) {
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
return {error};
}
};
export const updateThreadFollowing = async (serverUrl: string, teamId: string, threadId: string, state: boolean) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;

View file

@ -145,9 +145,12 @@ const ClientPosts = (superclass: any) => class extends superclass {
markPostAsUnread = async (userId: string, postId: string) => {
this.analytics.trackAPI('api_post_set_unread_post');
// collapsed_threads_supported is not based on user preferences but to know if "CLIENT" supports CRT
const body = JSON.stringify({collapsed_threads_supported: true});
return this.doFetch(
`${this.getUserRoute(userId)}/posts/${postId}/set_unread`,
{method: 'post'},
{method: 'post', body},
);
};

View file

@ -8,6 +8,7 @@ import {PER_PAGE_DEFAULT} from './constants';
export interface ClientThreadsMix {
getThreads: (userId: string, teamId: string, before?: string, after?: string, pageSize?: number, deleted?: boolean, unread?: boolean, since?: number, totalsOnly?: boolean, serverVersion?: string) => Promise<GetUserThreadsResponse>;
getThread: (userId: string, teamId: string, threadId: string, extended?: boolean) => Promise<any>;
markThreadAsUnread: (userId: string, teamId: string, threadId: string, postId: string) => Promise<any>;
updateTeamThreadsAsRead: (userId: string, teamId: string) => Promise<any>;
updateThreadRead: (userId: string, teamId: string, threadId: string, timestamp: number) => Promise<any>;
updateThreadFollow: (userId: string, teamId: string, threadId: string, state: boolean) => Promise<any>;
@ -42,6 +43,14 @@ const ClientThreads = (superclass: any) => class extends superclass {
);
};
markThreadAsUnread = (userId: string, teamId: string, threadId: string, postId: string) => {
const url = `${this.getThreadRoute(userId, teamId, threadId)}/set_unread/${postId}`;
return this.doFetch(
url,
{method: 'post'},
);
};
updateTeamThreadsAsRead = (userId: string, teamId: string) => {
const url = `${this.getThreadsRoute(userId, teamId)}/read`;
return this.doFetch(

View file

@ -68,11 +68,11 @@ export const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
color: changeOpacity(theme.sidebarText, 0.4),
},
badge: {
borderColor: theme.sidebarBg,
position: 'relative',
left: 0,
top: -2,
alignSelf: undefined,
borderColor: theme.sidebarBg,
},
infoBadge: {
color: theme.buttonColor,

View file

@ -131,7 +131,10 @@ export const transformMyChannelRecord = async ({action, database, value}: Transf
const fieldsMapper = (myChannel: MyChannelModel) => {
myChannel._raw.id = isCreateAction ? (raw.channel_id || myChannel.id) : record.id;
myChannel.roles = raw.roles;
myChannel.messageCount = isCRTEnabled ? raw.msg_count_root! : raw.msg_count;
// ignoring msg_count_root because msg_count is already calculated in "handleMyChannel" based on CRT is enabled or not
myChannel.messageCount = raw.msg_count;
myChannel.isUnread = Boolean(raw.is_unread);
myChannel.mentionsCount = isCRTEnabled ? raw.mention_count_root! : raw.mention_count;
myChannel.lastPostAt = (isCRTEnabled ? (raw.last_root_post_at || raw.last_post_at) : raw.last_post_at) || 0;

View file

@ -54,6 +54,9 @@ exports[`Threads Component should match snapshot 1`] = `
<Text
style={
Array [
Object {
"flex": 1,
},
Object {
"fontFamily": "OpenSans",
"fontSize": 16,

View file

@ -30,6 +30,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
iconActive: {
color: theme.sidebarText,
},
text: {
flex: 1,
},
}));
type Props = {
@ -67,6 +70,7 @@ const ThreadsButton = ({currentChannelId, unreadsAndMentions}: Props) => {
];
const text = [
customStyles.text,
unreads ? channelItemTextStyle.bright : channelItemTextStyle.regular,
styles.text,
unreads ? styles.highlight : undefined,

View file

@ -0,0 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {observeCurrentTeamId} from '@queries/servers/system';
import MarkAsUnreadOption from './mark_unread_option';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
return {
teamId: observeCurrentTeamId(database),
};
});
export default withDatabase(enhanced(MarkAsUnreadOption));

View file

@ -4,23 +4,33 @@
import React, {useCallback} from 'react';
import {markPostAsUnread} from '@actions/remote/post';
import {markThreadAsUnread} from '@actions/remote/thread';
import {BaseOption} from '@components/common_post_options';
import Screens from '@constants/screens';
import {useServerUrl} from '@context/server';
import {t} from '@i18n';
import {dismissBottomSheet} from '@screens/navigation';
import type PostModel from '@typings/database/models/servers/post';
type Props = {
postId: string;
location: typeof Screens[keyof typeof Screens];
post: PostModel;
teamId: string;
}
const MarkAsUnreadOption = ({postId}: Props) => {
const MarkAsUnreadOption = ({location, post, teamId}: Props) => {
const serverUrl = useServerUrl();
const onPress = useCallback(async () => {
await dismissBottomSheet(Screens.POST_OPTIONS);
markPostAsUnread(serverUrl, postId);
}, [serverUrl, postId]);
if (location === Screens.THREAD) {
const threadId = post.rootId || post.id;
markThreadAsUnread(serverUrl, teamId, threadId, post.id);
} else {
markPostAsUnread(serverUrl, post.id);
}
}, [location, post, serverUrl, teamId]);
return (
<BaseOption

View file

@ -94,7 +94,10 @@ const PostOptions = ({
<FollowThreadOption thread={thread}/>
}
{canMarkAsUnread && !isSystemPost &&
<MarkAsUnreadOption postId={post.id}/>
<MarkAsUnreadOption
post={post}
location={location}
/>
}
{canCopyPermalink && <CopyPermalinkOption post={post}/>}
{!isSystemPost &&

View file

@ -3,29 +3,30 @@
import React, {useCallback} from 'react';
import {updateThreadRead} from '@actions/remote/thread';
import {markThreadAsUnread, updateThreadRead} from '@actions/remote/thread';
import {BaseOption} from '@components/common_post_options';
import {Screens} from '@constants';
import {useServerUrl} from '@context/server';
import {t} from '@i18n';
import {dismissBottomSheet} from '@screens/navigation';
import type PostModel from '@typings/database/models/servers/post';
import type ThreadModel from '@typings/database/models/servers/thread';
type Props = {
post: PostModel;
teamId: string;
thread: ThreadModel;
}
const MarkAsUnreadOption = ({teamId, thread, post}: Props) => {
const MarkAsUnreadOption = ({teamId, thread}: Props) => {
const serverUrl = useServerUrl();
const onHandlePress = useCallback(async () => {
await dismissBottomSheet(Screens.THREAD_OPTIONS);
const timestamp = thread.unreadReplies ? Date.now() : post.createAt;
updateThreadRead(serverUrl, teamId, thread.id, timestamp);
}, [serverUrl, thread]);
if (thread.unreadReplies) {
updateThreadRead(serverUrl, teamId, thread.id, Date.now());
} else {
markThreadAsUnread(serverUrl, teamId, thread.id, thread.id);
}
}, [serverUrl, teamId, thread]);
const id = thread.unreadReplies ? t('global_threads.options.mark_as_read') : t('mobile.post_info.mark_unread');
const defaultMessage = thread.unreadReplies ? 'Mark as Read' : 'Mark as Unread';

View file

@ -92,7 +92,6 @@ const ThreadOptions = ({
key='mark-as-unread'
teamId={team.id}
thread={thread}
post={post}
/>,
<SaveOption
key='save'