Skip to content

Commit 2d1067c

Browse files
Fix GM/DM unread threads highlighting all teams as unreads (#8905) (#8933)
* Fix GM/DM unread threads highlighting all teams as unreads * Use options object instead of boolean flags --------- Co-authored-by: Mattermost Build <[email protected]>
1 parent 0437f72 commit 2d1067c

File tree

8 files changed

+202
-22
lines changed

8 files changed

+202
-22
lines changed

app/actions/local/thread.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
249249
export async function markTeamThreadsAsRead(serverUrl: string, teamId: string, prepareRecordsOnly = false) {
250250
try {
251251
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
252-
const threads = await queryThreadsInTeam(database, teamId, true, true, true).fetch();
252+
const threads = await queryThreadsInTeam(database, teamId, {onlyUnreads: true, hasReplies: true, isFollowing: true}).fetch();
253253
const models = threads.map((thread) => thread.prepareUpdate((record) => {
254254
record.unreadMentions = 0;
255255
record.unreadReplies = 0;

app/components/threads_button/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {switchMap} from 'rxjs/operators';
66

77
import {observeCurrentChannelId, observeCurrentTeamId} from '@queries/servers/system';
88
import {observeTeamLastChannelId} from '@queries/servers/team';
9-
import {observeUnreadsAndMentionsInTeam} from '@queries/servers/thread';
9+
import {observeUnreadsAndMentions} from '@queries/servers/thread';
1010

1111
import ThreadsButton from './threads_button';
1212

@@ -24,7 +24,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
2424
),
2525
unreadsAndMentions: currentTeamId.pipe(
2626
switchMap(
27-
(teamId) => observeUnreadsAndMentionsInTeam(database, teamId),
27+
(teamId) => observeUnreadsAndMentions(database, {teamId, includeDmGm: true}),
2828
),
2929
),
3030
};

app/database/subscription/unreads.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {MM_TABLES} from '@constants/database';
99
import DatabaseManager from '@database/manager';
1010
import {observeAllMyChannelNotifyProps} from '@queries/servers/channel';
1111
import {queryMyTeams} from '@queries/servers/team';
12-
import {getIsCRTEnabled, observeThreadMentionCount, queryThreads, observeUnreadsAndMentionsInTeam} from '@queries/servers/thread';
12+
import {getIsCRTEnabled, observeThreadMentionCount, queryThreads, observeUnreadsAndMentions} from '@queries/servers/thread';
1313

1414
import type MyChannelModel from '@typings/database/models/servers/my_channel';
1515

@@ -41,7 +41,7 @@ export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: Un
4141
observeWithColumns(['is_unread', 'mentions_count']).
4242
pipe(
4343
combineLatestWith(observeAllMyChannelNotifyProps(server.database)),
44-
combineLatestWith(observeUnreadsAndMentionsInTeam(server.database, undefined, true)),
44+
combineLatestWith(observeUnreadsAndMentions(server.database, {includeDmGm: true})),
4545
map$(([[myChannels, settings], {unreads, mentions}]) => ({myChannels, settings, threadUnreads: unreads, threadMentionCount: mentions})),
4646
).
4747
subscribe(observer);
@@ -60,7 +60,7 @@ export const subscribeMentionsByServer = (serverUrl: string, observer: ServerUnr
6060
query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))).
6161
observeWithColumns(['mentions_count']).
6262
pipe(
63-
combineLatestWith(observeThreadMentionCount(server.database, undefined, true)),
63+
combineLatestWith(observeThreadMentionCount(server.database, {includeDmGm: true})),
6464
map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})),
6565
).
6666
subscribe(observer.bind(undefined, serverUrl));
@@ -79,7 +79,7 @@ export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer:
7979
observeWithColumns(['mentions_count', 'is_unread']).
8080
pipe(
8181
combineLatestWith(observeAllMyChannelNotifyProps(server.database)),
82-
combineLatestWith(observeUnreadsAndMentionsInTeam(server.database, undefined, true)),
82+
combineLatestWith(observeUnreadsAndMentions(server.database, {includeDmGm: true})),
8383
map$(([[myChannels, settings], {unreads, mentions}]) => ({myChannels, settings, threadUnreads: unreads, threadMentionCount: mentions})),
8484
).
8585
subscribe(observer.bind(undefined, serverUrl));
@@ -108,7 +108,7 @@ export const getTotalMentionsForServer = async (serverUrl: string) => {
108108
let includeDmGm = true;
109109
const myTeamIds = await queryMyTeams(database).fetchIds();
110110
for await (const teamId of myTeamIds) {
111-
const threads = await queryThreads(database, teamId, false, includeDmGm).extend(
111+
const threads = await queryThreads(database, {teamId, includeDmGm}).extend(
112112
Q.where('unread_mentions', Q.gt(0)),
113113
).fetch();
114114
includeDmGm = false;

app/queries/servers/team.test.ts

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* eslint-disable max-lines */
55

66
import {processReceivedThreads} from '@actions/local/thread';
7-
import {Config, Preferences, Screens} from '@constants';
7+
import {ActionType, Config, Preferences, Screens} from '@constants';
88
import {SYSTEM_IDENTIFIERS} from '@constants/database';
99
import DatabaseManager from '@database/manager';
1010
import TestHelper from '@test/test_helper';
@@ -1219,6 +1219,146 @@ describe('Team Queries', () => {
12191219
await TestHelper.wait(waitTime);
12201220
expect(subscriptionNext).not.toHaveBeenCalled();
12211221
});
1222+
1223+
it('should not consider the team unread when a gm or a dm are unread', async () => {
1224+
const subscriptionNext = jest.fn();
1225+
const notify_props = {mark_unread: 'all' as const};
1226+
const result = observeIsTeamUnread(database, teamId);
1227+
1228+
result.subscribe({next: subscriptionNext});
1229+
await TestHelper.wait(waitTime);
1230+
1231+
// The subscription always return the first value
1232+
expect(subscriptionNext).toHaveBeenCalledWith(false);
1233+
subscriptionNext.mockClear();
1234+
1235+
// Setup DM channel with unreads
1236+
let channelModels = (await Promise.all((await prepareAllMyChannels(
1237+
operator,
1238+
[TestHelper.fakeChannel({id: 'dm1', team_id: '', type: 'D', total_msg_count: 30})],
1239+
[TestHelper.fakeMyChannel({
1240+
channel_id: 'dm1',
1241+
user_id: userId,
1242+
notify_props,
1243+
msg_count: 20,
1244+
})],
1245+
false,
1246+
)))).flat();
1247+
await operator.batchRecords([...channelModels], 'test');
1248+
1249+
await TestHelper.wait(waitTime);
1250+
expect(subscriptionNext).not.toHaveBeenCalled();
1251+
1252+
// Setup GM channel with unreads
1253+
channelModels = (await Promise.all((await prepareAllMyChannels(
1254+
operator,
1255+
[TestHelper.fakeChannel({id: 'gm1', team_id: '', type: 'G', total_msg_count: 30})],
1256+
[TestHelper.fakeMyChannel({
1257+
channel_id: 'gm1',
1258+
user_id: userId,
1259+
notify_props,
1260+
msg_count: 20,
1261+
})],
1262+
false,
1263+
)))).flat();
1264+
await operator.batchRecords([...channelModels], 'test');
1265+
1266+
await TestHelper.wait(waitTime);
1267+
expect(subscriptionNext).not.toHaveBeenCalled();
1268+
});
1269+
1270+
it('should not consider the team unread if a thread in a dm or a gm has unread messages', async () => {
1271+
const subscriptionNext = jest.fn();
1272+
const notify_props = {mark_unread: 'all' as const};
1273+
const result = observeIsTeamUnread(database, teamId);
1274+
1275+
result.subscribe({next: subscriptionNext});
1276+
await TestHelper.wait(waitTime);
1277+
1278+
// The subscription always return the first value
1279+
expect(subscriptionNext).toHaveBeenCalledWith(false);
1280+
subscriptionNext.mockClear();
1281+
1282+
// Setup DM channel with thread
1283+
let channelModels = (await Promise.all((await prepareAllMyChannels(
1284+
operator,
1285+
[TestHelper.fakeChannel({id: 'dm1', team_id: '', type: 'D', total_msg_count: 20})],
1286+
[TestHelper.fakeMyChannel({
1287+
channel_id: 'dm1',
1288+
user_id: userId,
1289+
notify_props,
1290+
msg_count: 20,
1291+
})],
1292+
false,
1293+
)))).flat();
1294+
1295+
const dmPost = TestHelper.fakePost({id: 'dm_thread', channel_id: 'dm1'});
1296+
1297+
const dmThreads = [{
1298+
...TestHelper.fakeThread({
1299+
id: 'dm_thread',
1300+
participants: undefined,
1301+
reply_count: 2,
1302+
last_reply_at: 123,
1303+
is_following: true,
1304+
unread_replies: 2,
1305+
unread_mentions: 1,
1306+
}),
1307+
lastFetchedAt: 0,
1308+
}];
1309+
1310+
const dmModels = await operator.handleThreads({threads: dmThreads, prepareRecordsOnly: false});
1311+
let postModels = await operator.handlePosts({
1312+
actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL,
1313+
order: [],
1314+
posts: [dmPost],
1315+
prepareRecordsOnly: true,
1316+
});
1317+
await operator.batchRecords([...channelModels, ...dmModels, ...postModels], 'test');
1318+
1319+
await TestHelper.wait(waitTime);
1320+
expect(subscriptionNext).not.toHaveBeenCalled();
1321+
1322+
// Setup GM channel with thread
1323+
channelModels = (await Promise.all((await prepareAllMyChannels(
1324+
operator,
1325+
[TestHelper.fakeChannel({id: 'gm1', team_id: '', type: 'G', total_msg_count: 20})],
1326+
[TestHelper.fakeMyChannel({
1327+
channel_id: 'gm1',
1328+
user_id: userId,
1329+
notify_props,
1330+
msg_count: 20,
1331+
})],
1332+
false,
1333+
)))).flat();
1334+
1335+
const gmPost = TestHelper.fakePost({id: 'gm_thread', channel_id: 'gm1'});
1336+
1337+
const gmThreads = [{
1338+
...TestHelper.fakeThread({
1339+
id: 'gm_thread',
1340+
participants: undefined,
1341+
reply_count: 3,
1342+
last_reply_at: 123,
1343+
is_following: true,
1344+
unread_replies: 3,
1345+
unread_mentions: 2,
1346+
}),
1347+
lastFetchedAt: 123,
1348+
}];
1349+
1350+
const gmModels = await operator.handleThreads({threads: gmThreads, prepareRecordsOnly: false});
1351+
postModels = await operator.handlePosts({
1352+
actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL,
1353+
order: [],
1354+
posts: [gmPost],
1355+
prepareRecordsOnly: true,
1356+
});
1357+
await operator.batchRecords([...channelModels, ...gmModels, ...postModels], 'test');
1358+
1359+
await TestHelper.wait(waitTime);
1360+
expect(subscriptionNext).not.toHaveBeenCalled();
1361+
});
12221362
});
12231363

12241364
describe('observeSortedJoinedTeams', () => {

app/queries/servers/team.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {prepareDeleteCategory} from './categories';
1414
import {prepareDeleteChannel, getDefaultChannelForTeam, observeMyChannelMentionCount, observeMyChannelUnreads} from './channel';
1515
import {queryPreferencesByCategoryAndName} from './preference';
1616
import {patchTeamHistory, getConfig, getTeamHistory, observeCurrentTeamId, getCurrentTeamId} from './system';
17-
import {observeThreadMentionCount, observeUnreadsAndMentionsInTeam} from './thread';
17+
import {observeThreadMentionCount, observeUnreadsAndMentions} from './thread';
1818
import {getCurrentUser} from './user';
1919

2020
import type {MyChannelModel} from '@database/models/server';
@@ -416,7 +416,7 @@ export const observeCurrentTeam = (database: Database) => {
416416

417417
export function observeMentionCount(database: Database, teamId?: string, includeDmGm?: boolean): Observable<number> {
418418
const channelMentionCountObservable = observeMyChannelMentionCount(database, teamId);
419-
const threadMentionCountObservable = observeThreadMentionCount(database, teamId, includeDmGm);
419+
const threadMentionCountObservable = observeThreadMentionCount(database, {teamId, includeDmGm});
420420

421421
return channelMentionCountObservable.pipe(
422422
combineLatestWith(threadMentionCountObservable),
@@ -427,7 +427,7 @@ export function observeMentionCount(database: Database, teamId?: string, include
427427

428428
export function observeIsTeamUnread(database: Database, teamId: string): Observable<boolean> {
429429
const channelUnreads = observeMyChannelUnreads(database, teamId);
430-
const threadsUnreadsAndMentions = observeUnreadsAndMentionsInTeam(database, teamId);
430+
const threadsUnreadsAndMentions = observeUnreadsAndMentions(database, {teamId});
431431

432432
return channelUnreads.pipe(
433433
combineLatestWith(threadsUnreadsAndMentions),

app/queries/servers/thread.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,16 @@ export const observeTeamIdByThread = (database: Database, thread: ThreadModel) =
8787
return observeTeamIdByThreadId(database, thread.id);
8888
};
8989

90-
export const observeUnreadsAndMentionsInTeam = (database: Database, teamId?: string, includeDmGm?: boolean): Observable<{unreads: boolean; mentions: number}> => {
91-
const observeThreads = () => queryThreads(database, teamId, true, includeDmGm).
90+
type ObserveUnreadsAndMentionsOptions = {
91+
teamId?: string;
92+
includeDmGm?: boolean;
93+
};
94+
95+
export const observeUnreadsAndMentions = (database: Database, {
96+
teamId,
97+
includeDmGm,
98+
}: ObserveUnreadsAndMentionsOptions): Observable<{unreads: boolean; mentions: number}> => {
99+
const observeThreads = () => queryThreads(database, {teamId, onlyUnreads: true, includeDmGm}).
92100
observeWithColumns(['unread_replies', 'unread_mentions']).
93101
pipe(
94102
switchMap((threads) => {
@@ -154,7 +162,21 @@ export const prepareThreadsFromReceivedPosts = async (operator: ServerDataOperat
154162
return models;
155163
};
156164

157-
export const queryThreadsInTeam = (database: Database, teamId: string, onlyUnreads?: boolean, hasReplies?: boolean, isFollowing?: boolean, sort?: boolean, earliest?: number): Query<ThreadModel> => {
165+
type QueryThreadsInTeamOptions = {
166+
onlyUnreads?: boolean;
167+
hasReplies?: boolean;
168+
isFollowing?: boolean;
169+
sort?: boolean;
170+
earliest?: number;
171+
};
172+
173+
export const queryThreadsInTeam = (database: Database, teamId: string, {
174+
onlyUnreads,
175+
hasReplies,
176+
isFollowing,
177+
sort,
178+
earliest,
179+
}: QueryThreadsInTeamOptions): Query<ThreadModel> => {
158180
const query: Q.Clause[] = [
159181
Q.experimentalNestedJoin(POST, CHANNEL),
160182
Q.on(POST, Q.on(CHANNEL, Q.where('delete_at', 0))),
@@ -193,14 +215,32 @@ export const queryTeamThreadsSync = (database: Database, teamId: string) => {
193215
);
194216
};
195217

196-
export function observeThreadMentionCount(database: Database, teamId?: string, includeDmGm?: boolean): Observable<number> {
197-
return observeUnreadsAndMentionsInTeam(database, teamId, includeDmGm).pipe(
218+
type ObserveThreadMentionCountOptions = {
219+
teamId?: string;
220+
includeDmGm?: boolean;
221+
};
222+
223+
export function observeThreadMentionCount(database: Database, {
224+
teamId,
225+
includeDmGm,
226+
}: ObserveThreadMentionCountOptions): Observable<number> {
227+
return observeUnreadsAndMentions(database, {teamId, includeDmGm}).pipe(
198228
switchMap(({mentions}) => of$(mentions)),
199229
distinctUntilChanged(),
200230
);
201231
}
202232

203-
export const queryThreads = (database: Database, teamId?: string, onlyUnreads = false, includeDmGm = true): Query<ThreadModel> => {
233+
type QueryThreadsOptions = {
234+
teamId?: string;
235+
onlyUnreads?: boolean;
236+
includeDmGm?: boolean;
237+
};
238+
239+
export const queryThreads = (database: Database, {
240+
teamId,
241+
onlyUnreads,
242+
includeDmGm,
243+
}: QueryThreadsOptions): Query<ThreadModel> => {
204244
const query: Q.Clause[] = [
205245
Q.where('is_following', true),
206246
Q.where('reply_count', Q.gt(0)),

app/screens/global_threads/threads_list/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ const enhanced = withObservables(['tab', 'teamId'], ({database, tab, teamId}: Pr
2727
const teamThreadsSyncObserver = queryTeamThreadsSync(database, teamId).observeWithColumns(['earliest']);
2828

2929
return {
30-
unreadsCount: queryThreadsInTeam(database, teamId, true, true, true).observeCount(false),
30+
unreadsCount: queryThreadsInTeam(database, teamId, {onlyUnreads: true, hasReplies: true, isFollowing: true}).observeCount(false),
3131
teammateNameDisplay: observeTeammateNameDisplay(database),
3232
threads: teamThreadsSyncObserver.pipe(
3333
switchMap((teamThreadsSync) => {
3434
const earliest = tab === 'all' ? teamThreadsSync?.[0]?.earliest : 0;
35-
return queryThreadsInTeam(database, teamId, getOnlyUnreads, true, true, true, earliest).observe();
35+
return queryThreadsInTeam(database, teamId, {onlyUnreads: getOnlyUnreads, hasReplies: true, isFollowing: true, sort: true, earliest}).observe();
3636
}),
3737
),
3838
};

app/screens/home/channel_list/categories_list/categories/unreads/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {filterAndSortMyChannels, makeChannelsMap} from '@helpers/database';
1111
import {getChannelById, observeChannelsByLastPostAt, observeNotifyPropsByChannels, queryMyChannelUnreads} from '@queries/servers/channel';
1212
import {querySidebarPreferences} from '@queries/servers/preference';
1313
import {observeLastUnreadChannelId} from '@queries/servers/system';
14-
import {observeUnreadsAndMentionsInTeam} from '@queries/servers/thread';
14+
import {observeUnreadsAndMentions} from '@queries/servers/thread';
1515

1616
import UnreadCategories from './unreads';
1717

@@ -62,7 +62,7 @@ const enhanced = withObservables(['currentTeamId', 'isTablet', 'onlyUnreads'], (
6262
}
6363
return of$([]);
6464
}));
65-
const unreadThreads = observeUnreadsAndMentionsInTeam(database, currentTeamId, true);
65+
const unreadThreads = observeUnreadsAndMentions(database, {teamId: currentTeamId, includeDmGm: true});
6666

6767
return {
6868
unreadChannels,

0 commit comments

Comments
 (0)