[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:
parent
251ef0992b
commit
2deb2e01e0
13 changed files with 119 additions and 23 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ exports[`Threads Component should match snapshot 1`] = `
|
|||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
},
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
19
app/screens/post_options/options/mark_unread_option/index.ts
Normal file
19
app/screens/post_options/options/mark_unread_option/index.ts
Normal 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));
|
||||
|
|
@ -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
|
||||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ const ThreadOptions = ({
|
|||
key='mark-as-unread'
|
||||
teamId={team.id}
|
||||
thread={thread}
|
||||
post={post}
|
||||
/>,
|
||||
<SaveOption
|
||||
key='save'
|
||||
|
|
|
|||
Loading…
Reference in a new issue