From df3ef72a0a686bd1055a84c04db18ca17fa8b839 Mon Sep 17 00:00:00 2001 From: Kyriakos Z <3829551+koox00@users.noreply.github.com> Date: Tue, 12 Apr 2022 13:27:40 +0300 Subject: [PATCH] [Gekidou CRT] thread mention counts (#6126) * Sets values on my_channel according to CRT * Team counts with regard to CRT * Fixes myChannel.is_unread with regard to CRT * Include DM/GMs for thread counts on demand * Incorporate thread mention counts in server/channel * Channel updates in regard to CRT --- app/actions/local/thread.ts | 2 +- app/actions/remote/post.ts | 32 ++++++-- app/actions/websocket/posts.ts | 30 ++++++-- .../team_sidebar/team_list/team_item/index.ts | 7 +- .../server_data_operator/handlers/channel.ts | 8 +- .../transformers/channel.ts | 11 ++- app/database/subscription/unreads.ts | 34 ++++++-- app/queries/servers/channel.ts | 27 ++++++- app/queries/servers/team.ts | 25 +++++- app/queries/servers/thread.ts | 77 +++++++++++++++---- .../channel/other_mentions_badge/index.tsx | 4 +- .../home/channel_list/servers/index.tsx | 4 +- .../servers_list/server_item/server_item.tsx | 3 +- app/screens/home/tab_bar/home.tsx | 4 +- types/api/channels.d.ts | 2 + 15 files changed, 212 insertions(+), 58 deletions(-) diff --git a/app/actions/local/thread.ts b/app/actions/local/thread.ts index 5df8990548..525703da22 100644 --- a/app/actions/local/thread.ts +++ b/app/actions/local/thread.ts @@ -186,7 +186,7 @@ export async function markTeamThreadsAsRead(serverUrl: string, teamId: string, p } try { const {database} = operator; - const threads = await queryThreadsInTeam(database, teamId, true).fetch(); + const threads = await queryThreadsInTeam(database, teamId, true, true, true).fetch(); const models = threads.map((thread) => thread.prepareUpdate((record) => { record.unreadMentions = 0; record.unreadReplies = 0; diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 784836af69..abf2267558 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -226,6 +226,8 @@ export async function fetchPostsForChannel(serverUrl: string, channelId: string, } if (!fetchOnly) { + const isCRTEnabled = await getIsCRTEnabled(operator.database); + const models = []; const postModels = await operator.handlePosts({ actionType, @@ -243,14 +245,18 @@ export async function fetchPostsForChannel(serverUrl: string, channelId: string, let lastPostAt = 0; for (const post of data.posts) { - lastPostAt = post.create_at > lastPostAt ? post.create_at : lastPostAt; - } - const {member: memberModel} = await updateLastPostAt(serverUrl, channelId, lastPostAt, true); - if (memberModel) { - models.push(memberModel); + if (!isCRTEnabled || post.root_id) { + lastPostAt = post.create_at > lastPostAt ? post.create_at : lastPostAt; + } + } + + if (lastPostAt) { + const {member: memberModel} = await updateLastPostAt(serverUrl, channelId, lastPostAt, true); + if (memberModel) { + models.push(memberModel); + } } - const isCRTEnabled = await getIsCRTEnabled(operator.database); if (isCRTEnabled) { const threadModels = await prepareThreadsFromReceivedPosts(operator, data.posts); if (threadModels?.length) { @@ -841,8 +847,18 @@ export const markPostAsUnread = async (serverUrl: string, postId: string) => { client.getChannelMember(channelId, userId), ]); if (channel && channelMember) { - const messageCount = channel.total_msg_count - channelMember.msg_count; - const mentionCount = channelMember.mention_count; + const isCRTEnabled = await getIsCRTEnabled(database); + let totalMessages = channel.total_msg_count; + let messages = channelMember.msg_count; + let mentionCount = channelMember.mention_count; + + if (isCRTEnabled) { + totalMessages = channel.total_msg_count_root!; + messages = channelMember.msg_count_root!; + mentionCount = channelMember.mention_count_root!; + } + + const messageCount = totalMessages - messages; await markChannelAsUnread(serverUrl, channelId, messageCount, mentionCount, post.createAt); return { post, diff --git a/app/actions/websocket/posts.ts b/app/actions/websocket/posts.ts index 9ca64ede2d..8a2b161e20 100644 --- a/app/actions/websocket/posts.ts +++ b/app/actions/websocket/posts.ts @@ -149,7 +149,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag if (viewedAt) { models.push(viewedAt); } - } else { + } else if (!isCRTEnabled || !post.root_id) { const hasMentions = msg.data.mentions?.includes(currentUserId); preparedMyChannelHack(myChannel); const {member: unreadAt} = await markChannelAsUnread( @@ -246,18 +246,36 @@ export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage export async function handlePostUnread(serverUrl: string, msg: WebSocketMessage) { const {channel_id: channelId, team_id: teamId} = msg.broadcast; - const {mention_count: mentionCount, msg_count: msgCount, last_viewed_at: lastViewedAt} = msg.data; + const { + mention_count: mentionCount, + mention_count_root: mentionCountRoot, + msg_count: msgCount, + msg_count_root: msgCountRoot, + last_viewed_at: lastViewedAt, + } = msg.data; + const database = DatabaseManager.serverDatabases[serverUrl]?.database; if (!database) { return; } + const [myChannel, isCRTEnabled] = await Promise.all([ + getMyChannel(database, channelId), + getIsCRTEnabled(database), + ]); + + let messages = msgCount; + let mentions = mentionCount; + if (isCRTEnabled) { + messages = msgCountRoot; + mentions = mentionCountRoot; + } - const myChannel = await getMyChannel(database, channelId); if (!myChannel?.manuallyUnread) { const {channels} = await fetchMyChannel(serverUrl, teamId, channelId, true); const channel = channels?.[0]; - const postNumber = channel?.total_msg_count; - const delta = postNumber ? postNumber - msgCount : msgCount; - markChannelAsUnread(serverUrl, channelId, delta, mentionCount, lastViewedAt); + const postNumber = isCRTEnabled ? channel?.total_msg_count_root : channel?.total_msg_count; + const delta = postNumber ? postNumber - messages : messages; + + markChannelAsUnread(serverUrl, channelId, delta, mentions, lastViewedAt); } } diff --git a/app/components/team_sidebar/team_list/team_item/index.ts b/app/components/team_sidebar/team_list/team_item/index.ts index 1a630b3d79..bc21da0025 100644 --- a/app/components/team_sidebar/team_list/team_item/index.ts +++ b/app/components/team_sidebar/team_list/team_item/index.ts @@ -8,6 +8,7 @@ import {switchMap} from 'rxjs/operators'; import {queryMyChannelsByTeam} from '@queries/servers/channel'; import {observeCurrentTeamId} from '@queries/servers/system'; +import {observeMentionCount} from '@queries/servers/team'; import TeamItem from './team_item'; @@ -20,10 +21,6 @@ type WithTeamsArgs = WithDatabaseArgs & { const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) => { const myChannels = queryMyChannelsByTeam(database, myTeam.id).observeWithColumns(['mentions_count', 'is_unread']); - const mentionCount = myChannels.pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((val) => of$(val.reduce((acc, v) => acc + v.mentionsCount, 0))), - ); const hasUnreads = myChannels.pipe( // eslint-disable-next-line max-nested-callbacks switchMap((val) => of$(val.reduce((acc, v) => acc || v.isUnread, false))), @@ -32,7 +29,7 @@ const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) return { currentTeamId: observeCurrentTeamId(database), team: myTeam.team.observe(), - mentionCount, + mentionCount: observeMentionCount(database, myTeam.id, false), hasUnreads, }; }); diff --git a/app/database/operator/server_data_operator/handlers/channel.ts b/app/database/operator/server_data_operator/handlers/channel.ts index 75152653dc..fc24c564e3 100644 --- a/app/database/operator/server_data_operator/handlers/channel.ts +++ b/app/database/operator/server_data_operator/handlers/channel.ts @@ -14,6 +14,7 @@ import { transformMyChannelSettingsRecord, } from '@database/operator/server_data_operator/transformers/channel'; import {getUniqueRawsBy} from '@database/operator/utils/general'; +import {getIsCRTEnabled} from '@queries/servers/thread'; import type {HandleChannelArgs, HandleChannelInfoArgs, HandleChannelMembershipArgs, HandleMyChannelArgs, HandleMyChannelSettingsArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; @@ -156,14 +157,19 @@ const ChannelHandler = (superclass: any) => class extends superclass { return []; } + const isCRTEnabled = await getIsCRTEnabled(this.database); + const channelMap = channels.reduce((result: Record, channel) => { result[channel.id] = channel; return result; }, {}); + for (const my of myChannels) { const channel = channelMap[my.channel_id]; if (channel) { - const msgCount = Math.max(0, channel.total_msg_count - my.msg_count); + const total = isCRTEnabled ? channel.total_msg_count_root! : channel.total_msg_count; + const myMsgCount = isCRTEnabled ? my.msg_count_root! : my.msg_count; + const msgCount = Math.max(0, total - myMsgCount); my.msg_count = msgCount; my.is_unread = msgCount > 0; } diff --git a/app/database/operator/server_data_operator/transformers/channel.ts b/app/database/operator/server_data_operator/transformers/channel.ts index 4fff9ece83..4d54ca05cc 100644 --- a/app/database/operator/server_data_operator/transformers/channel.ts +++ b/app/database/operator/server_data_operator/transformers/channel.ts @@ -4,6 +4,7 @@ import {MM_TABLES} from '@constants/database'; import {prepareBaseRecord} from '@database/operator/server_data_operator/transformers/index'; import {extractChannelDisplayName} from '@helpers/database'; +import {getIsCRTEnabled} from '@queries/servers/thread'; import {OperationType} from '@typings/database/enums'; import type {TransformerArgs} from '@typings/database/database'; @@ -120,18 +121,20 @@ export const transformChannelInfoRecord = ({action, database, value}: Transforme * @param {RecordPair} operator.value * @returns {Promise} */ -export const transformMyChannelRecord = ({action, database, value}: TransformerArgs): Promise => { +export const transformMyChannelRecord = async ({action, database, value}: TransformerArgs): Promise => { const raw = value.raw as ChannelMembership; const record = value.record as MyChannelModel; const isCreateAction = action === OperationType.CREATE; + const isCRTEnabled = await getIsCRTEnabled(database); + const fieldsMapper = (myChannel: MyChannelModel) => { myChannel._raw.id = isCreateAction ? (raw.channel_id || myChannel.id) : record.id; myChannel.roles = raw.roles; - myChannel.messageCount = raw.msg_count; + myChannel.messageCount = isCRTEnabled ? raw.msg_count_root! : raw.msg_count; myChannel.isUnread = Boolean(raw.is_unread); - myChannel.mentionsCount = raw.mention_count; - myChannel.lastPostAt = raw.last_post_at || 0; + 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; myChannel.lastViewedAt = raw.last_viewed_at; myChannel.viewedAt = record?.viewedAt || 0; }; diff --git a/app/database/subscription/unreads.ts b/app/database/subscription/unreads.ts index e203004525..0c0a897df6 100644 --- a/app/database/subscription/unreads.ts +++ b/app/database/subscription/unreads.ts @@ -2,16 +2,23 @@ // See LICENSE.txt for license information. import {Q} from '@nozbe/watermelondb'; +import {map as map$, Subscription} from 'rxjs'; +import {combineLatestWith} from 'rxjs/operators'; import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; +import {observeThreadMentionCount} from '@queries/servers/thread'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; -import type {Subscription} from 'rxjs'; const {SERVER: {CHANNEL, MY_CHANNEL}} = MM_TABLES; -export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: (myChannels: MyChannelModel[]) => void) => { +type ObserverArgs = { + myChannels: MyChannelModel[]; + threadMentionCount: number; +} + +export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: ({myChannels, threadMentionCount}: ObserverArgs) => void) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; @@ -19,34 +26,47 @@ export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: (m subscription = server.database.get(MY_CHANNEL). query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). observeWithColumns(['is_unread', 'mentions_count']). + pipe( + combineLatestWith(observeThreadMentionCount(server.database, undefined, false)), + map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})), + ). subscribe(observer); } return subscription; }; -export const subscribeMentionsByServer = (serverUrl: string, observer: (serverUrl: string, myChannels: MyChannelModel[]) => void) => { +export const subscribeMentionsByServer = (serverUrl: string, observer: (serverUrl: string, {myChannels, threadMentionCount}: ObserverArgs) => void) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; + if (server?.database) { subscription = server.database. get(MY_CHANNEL). query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). observeWithColumns(['mentions_count']). + pipe( + combineLatestWith(observeThreadMentionCount(server.database, undefined, false)), + map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})), + ). subscribe(observer.bind(undefined, serverUrl)); } return subscription; }; -export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer: (serverUrl: string, myChannels: MyChannelModel[]) => void) => { +export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer: (serverUrl: string, {myChannels, threadMentionCount}: ObserverArgs) => void) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; + if (server?.database) { - subscription = server.database. - get(MY_CHANNEL). + subscription = server.database.get(MY_CHANNEL). query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). - observeWithColumns(['mentions_count', 'has_unreads']). + observeWithColumns(['mentions_count', 'is_unread']). + pipe( + combineLatestWith(observeThreadMentionCount(server.database, undefined, false)), + map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})), + ). subscribe(observer.bind(undefined, serverUrl)); } diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 320c93cb49..56e9d12ec9 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -2,8 +2,8 @@ // See LICENSE.txt for license information. import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {of as of$, Observable} from 'rxjs'; +import {switchMap, distinctUntilChanged} from 'rxjs/operators'; import {General, Permissions} from '@constants'; import {MM_TABLES} from '@constants/database'; @@ -372,3 +372,26 @@ export const queryMyChannelSettingsByIds = (database: Database, ids: string[]) = export const queryChannelsByNames = (database: Database, names: string[]) => { return database.get(CHANNEL).query(Q.where('name', Q.oneOf(names))); }; + +export function observeMyChannelMentionCount(database: Database, teamId?: string, columns = ['mentions_count', 'is_unread']): Observable { + const conditions: Q.Condition[] = [ + Q.where('delete_at', Q.eq(0)), + ]; + + if (teamId) { + conditions.push(Q.where('team_id', Q.eq(teamId))); + } + + return database.get(MY_CHANNEL).query( + Q.on(CHANNEL, Q.and( + ...conditions, + )), + ). + observeWithColumns(columns). + pipe( + switchMap((val) => of$(val.reduce((acc, v) => { + return acc + v.mentionsCount; + }, 0))), + distinctUntilChanged(), + ); +} diff --git a/app/queries/servers/team.ts b/app/queries/servers/team.ts index e38eafda86..37e4bff724 100644 --- a/app/queries/servers/team.ts +++ b/app/queries/servers/team.ts @@ -2,8 +2,8 @@ // See LICENSE.txt for license information. import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {of as of$, map as map$, Observable} from 'rxjs'; +import {switchMap, distinctUntilChanged, combineLatestWith} from 'rxjs/operators'; import {Database as DatabaseConstants, Preferences} from '@constants'; import {getPreferenceValue} from '@helpers/api/preference'; @@ -11,9 +11,10 @@ import {selectDefaultTeam} from '@helpers/api/team'; import {DEFAULT_LOCALE} from '@i18n'; import {prepareDeleteCategory} from './categories'; -import {prepareDeleteChannel, getDefaultChannelForTeam} from './channel'; +import {prepareDeleteChannel, getDefaultChannelForTeam, observeMyChannelMentionCount} from './channel'; import {queryPreferencesByCategoryAndName} from './preference'; import {patchTeamHistory, getConfig, getTeamHistory, observeCurrentTeamId} from './system'; +import {observeThreadMentionCount} from './thread'; import {getCurrentUser} from './user'; import type ServerDataOperator from '@database/operator/server_data_operator'; @@ -21,7 +22,12 @@ import type MyTeamModel from '@typings/database/models/servers/my_team'; import type TeamModel from '@typings/database/models/servers/team'; import type TeamChannelHistoryModel from '@typings/database/models/servers/team_channel_history'; -const {MY_TEAM, TEAM, TEAM_CHANNEL_HISTORY, MY_CHANNEL} = DatabaseConstants.MM_TABLES.SERVER; +const { + MY_CHANNEL, + MY_TEAM, + TEAM, + TEAM_CHANNEL_HISTORY, +} = DatabaseConstants.MM_TABLES.SERVER; export const addChannelToTeamHistory = async (operator: ServerDataOperator, teamId: string, channelId: string, prepareRecordsOnly = false) => { let tch: TeamChannelHistory|undefined; @@ -377,3 +383,14 @@ export const observeCurrentTeam = (database: Database) => { switchMap((id) => observeTeam(database, id)), ); }; + +export function observeMentionCount(database: Database, teamId?: string, includeDmGm?: boolean): Observable { + const channelMentionCountObservable = observeMyChannelMentionCount(database, teamId); + const threadMentionCountObservable = observeThreadMentionCount(database, teamId, includeDmGm); + + return channelMentionCountObservable.pipe( + combineLatestWith(threadMentionCountObservable), + map$(([ccount, tcount]) => ccount + tcount), + distinctUntilChanged(), + ); +} diff --git a/app/queries/servers/thread.ts b/app/queries/servers/thread.ts index 43ccdb0a78..dffc231677 100644 --- a/app/queries/servers/thread.ts +++ b/app/queries/servers/thread.ts @@ -2,8 +2,8 @@ // See LICENSE.txt for license information. import {Database, Q, Query} from '@nozbe/watermelondb'; -import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {combineLatest, of as of$, Observable} from 'rxjs'; +import {map, switchMap, distinctUntilChanged} from 'rxjs/operators'; import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; @@ -52,17 +52,24 @@ export const observeThreadById = (database: Database, threadId: string) => { ); }; -export const observeUnreadsAndMentionsInTeam = (database: Database, teamId: string) => { - return queryThreadsInTeam(database, teamId, true).observeWithColumns(['unread_replies', 'unread_mentions']).pipe( - switchMap((threads) => { - let unreads = 0; - let mentions = 0; - threads.forEach((thread) => { - unreads += thread.unreadReplies; - mentions += thread.unreadMentions; - }); - return of$({unreads, mentions}); - }), +export const observeUnreadsAndMentionsInTeam = (database: Database, teamId?: string, includeDmGm?: boolean): Observable<{unreads: number; mentions: number}> => { + const observeThreads = () => queryThreads(database, teamId, true, includeDmGm). + observeWithColumns(['unread_replies', 'unread_mentions']). + pipe( + switchMap((threads) => { + let unreads = 0; + let mentions = 0; + for (const thread of threads) { + unreads += thread.unreadReplies; + mentions += thread.unreadMentions; + } + + return of$({unreads, mentions}); + }), + ); + + return observeIsCRTEnabled(database).pipe( + switchMap((hasCRT) => (hasCRT ? observeThreads() : of$({unreads: 0, mentions: 0}))), ); }; @@ -125,3 +132,47 @@ export const queryThreadsInTeam = (database: Database, teamId: string, onlyUnrea return database.get(THREAD).query(...query); }; + +export function observeThreadMentionCount(database: Database, teamId?: string, includeDmGm?: boolean): Observable { + return observeUnreadsAndMentionsInTeam(database, teamId, includeDmGm).pipe( + switchMap(({mentions}) => of$(mentions)), + distinctUntilChanged(), + ); +} + +export const queryThreads = (database: Database, teamId?: string, onlyUnreads = false, includeDmGm = true): Query => { + const query: Q.Clause[] = [ + Q.where('is_following', true), + Q.where('reply_count', Q.gt(0)), + ]; + + // If teamId is specified, only get threads in that team + if (teamId) { + let condition: Q.Condition = Q.where('team_id', teamId); + + if (includeDmGm) { + condition = Q.or( + Q.where('team_id', teamId), + Q.where('team_id', ''), + ); + } + + query.push( + Q.experimentalNestedJoin(POST, CHANNEL), + Q.on(POST, Q.on(CHANNEL, condition)), + ); + } else if (!includeDmGm) { + // fetching all threads from all teams + // excluding DM/GM channels + query.push( + Q.experimentalNestedJoin(POST, CHANNEL), + Q.on(POST, Q.on(CHANNEL, Q.where('team_id', Q.notEq('')))), + ); + } + + if (onlyUnreads) { + query.push(Q.where('unread_replies', Q.gt(0))); + } + + return database.get(THREAD).query(...query); +}; diff --git a/app/screens/channel/other_mentions_badge/index.tsx b/app/screens/channel/other_mentions_badge/index.tsx index 2cafd064a1..a85408d6eb 100644 --- a/app/screens/channel/other_mentions_badge/index.tsx +++ b/app/screens/channel/other_mentions_badge/index.tsx @@ -37,7 +37,7 @@ const OtherMentionsBadge = ({channelId}: Props) => { setCount(mentions); }; - const unreadsSubscription = (serverUrl: string, myChannels: MyChannelModel[]) => { + const unreadsSubscription = (serverUrl: string, {myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; @@ -47,7 +47,7 @@ const OtherMentionsBadge = ({channelId}: Props) => { } } - unreads.mentions = mentions; + unreads.mentions = mentions + threadMentionCount; subscriptions.set(serverUrl, unreads); updateCount(); } diff --git a/app/screens/home/channel_list/servers/index.tsx b/app/screens/home/channel_list/servers/index.tsx index 5a8f900d88..1747595705 100644 --- a/app/screens/home/channel_list/servers/index.tsx +++ b/app/screens/home/channel_list/servers/index.tsx @@ -66,7 +66,7 @@ export default function Servers() { setTotal({mentions, unread}); }; - const unreadsSubscription = (serverUrl: string, myChannels: MyChannelModel[]) => { + const unreadsSubscription = (serverUrl: string, {myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; @@ -76,7 +76,7 @@ export default function Servers() { unread = unread || myChannel.isUnread; } - unreads.mentions = mentions; + unreads.mentions = mentions + threadMentionCount; unreads.unread = unread; subscriptions.set(serverUrl, unreads); updateTotal(); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx index 25d99e6989..913b477494 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx +++ b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx @@ -136,13 +136,14 @@ const ServerItem = ({highlight, isActive, server, tutorialWatched}: Props) => { displayName = intl.formatMessage({id: 'servers.default', defaultMessage: 'Default Server'}); } - const unreadsSubscription = (myChannels: MyChannelModel[]) => { + const unreadsSubscription = ({myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { let mentions = 0; let isUnread = false; for (const myChannel of myChannels) { mentions += myChannel.mentionsCount; isUnread = isUnread || myChannel.isUnread; } + mentions += threadMentionCount; setBadge({isUnread, mentions}); }; diff --git a/app/screens/home/tab_bar/home.tsx b/app/screens/home/tab_bar/home.tsx index 5d63d1a836..d88a9b7151 100644 --- a/app/screens/home/tab_bar/home.tsx +++ b/app/screens/home/tab_bar/home.tsx @@ -51,7 +51,7 @@ const Home = ({isFocused, theme}: Props) => { setTotal({mentions, unread}); }; - const unreadsSubscription = (serverUrl: string, myChannels: MyChannelModel[]) => { + const unreadsSubscription = (serverUrl: string, {myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; @@ -61,7 +61,7 @@ const Home = ({isFocused, theme}: Props) => { unread = unread || myChannel.isUnread; } - unreads.mentions = mentions; + unreads.mentions = mentions + threadMentionCount; unreads.unread = unread; subscriptions.set(serverUrl, unreads); updateTotal(); diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index 2447c4444a..5122c512b4 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -28,6 +28,7 @@ type Channel = { purpose: string; last_post_at: number; total_msg_count: number; + total_msg_count_root?: number; extra_update_at: number; creator_id: string; scheme_id: string|null; @@ -60,6 +61,7 @@ type ChannelMembership = { mention_count_root?: number; notify_props: Partial; last_post_at?: number; + last_root_post_at?: number; last_update_at: number; scheme_user?: boolean; scheme_admin?: boolean;