From ecfbb934a8011509f7bdb1218e4434c1cc8ed84d Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 4 May 2022 09:51:14 -0400 Subject: [PATCH] [Gekidou] Exclude muted channels from unread badges (#6208) * Exclude muted channels from unread badges * Gekidou unreads mention fix (#6221) * Removes muted channels without mentions from grouped unreads * show channel if unread and have no mentions when unread up top Co-authored-by: Elias Nahum Co-authored-by: Shaz MJ --- app/components/channel_item/index.ts | 2 +- .../team_sidebar/team_list/team_item/index.ts | 12 ++++++--- app/database/subscription/unreads.ts | 27 ++++++++++++++----- app/queries/servers/channel.ts | 15 ++++++++++- .../categories/unreads/index.ts | 18 +++++++++++-- .../home/channel_list/servers/index.tsx | 8 +++--- .../servers_list/server_item/server_item.tsx | 8 +++--- app/screens/home/tab_bar/home.tsx | 8 +++--- 8 files changed, 71 insertions(+), 27 deletions(-) diff --git a/app/components/channel_item/index.ts b/app/components/channel_item/index.ts index e6063e330..0b8ce6ba9 100644 --- a/app/components/channel_item/index.ts +++ b/app/components/channel_item/index.ts @@ -59,7 +59,7 @@ const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({chan return of$(u); } - return u ? of$(!mc.isUnread) : of$(true); + return u ? of$(!mc.isUnread || !mc.mentionsCount) : of$(true); }), ); 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 bc21da002..1a5b66c00 100644 --- a/app/components/team_sidebar/team_list/team_item/index.ts +++ b/app/components/team_sidebar/team_list/team_item/index.ts @@ -3,10 +3,9 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {combineLatestWith, map} from 'rxjs/operators'; -import {queryMyChannelsByTeam} from '@queries/servers/channel'; +import {observeAllMyChannelNotifyProps, queryMyChannelsByTeam} from '@queries/servers/channel'; import {observeCurrentTeamId} from '@queries/servers/system'; import {observeMentionCount} from '@queries/servers/team'; @@ -21,9 +20,14 @@ type WithTeamsArgs = WithDatabaseArgs & { const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) => { const myChannels = queryMyChannelsByTeam(database, myTeam.id).observeWithColumns(['mentions_count', 'is_unread']); + const notifyProps = observeAllMyChannelNotifyProps(database); const hasUnreads = myChannels.pipe( + combineLatestWith(notifyProps), // eslint-disable-next-line max-nested-callbacks - switchMap((val) => of$(val.reduce((acc, v) => acc || v.isUnread, false))), + map(([mycs, notify]) => mycs.reduce((acc, v) => { + const isMuted = notify?.[v.id]?.mark_unread === 'mention'; + return acc || (v.isUnread && !isMuted); + }, false)), ); return { diff --git a/app/database/subscription/unreads.ts b/app/database/subscription/unreads.ts index af0709817..01cef3697 100644 --- a/app/database/subscription/unreads.ts +++ b/app/database/subscription/unreads.ts @@ -7,18 +7,29 @@ import {combineLatestWith} from 'rxjs/operators'; import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; +import {observeAllMyChannelNotifyProps} from '@queries/servers/channel'; import {queryMyTeams} from '@queries/servers/team'; import {getIsCRTEnabled, observeThreadMentionCount, queryThreads} from '@queries/servers/thread'; -import MyChannelModel from '@typings/database/models/servers/my_channel'; + +import type MyChannelModel from '@typings/database/models/servers/my_channel'; const {SERVER: {CHANNEL, MY_CHANNEL}} = MM_TABLES; -type ObserverArgs = { +export type UnreadObserverArgs = { myChannels: MyChannelModel[]; + settings?: Record>; threadMentionCount: number; } -export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: ({myChannels, threadMentionCount}: ObserverArgs) => void) => { +type ServerUnreadObserver = { + (serverUrl: string, {myChannels, settings, threadMentionCount}: UnreadObserverArgs): void; +} + +type UnreadObserver = { + ({myChannels, settings, threadMentionCount}: UnreadObserverArgs): void; +} + +export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: UnreadObserver) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; @@ -27,8 +38,9 @@ export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: ({ query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). observeWithColumns(['is_unread', 'mentions_count']). pipe( + combineLatestWith(observeAllMyChannelNotifyProps(server.database)), combineLatestWith(observeThreadMentionCount(server.database, undefined, false)), - map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})), + map$(([[myChannels, settings], threadMentionCount]) => ({myChannels, settings, threadMentionCount})), ). subscribe(observer); } @@ -36,7 +48,7 @@ export const subscribeServerUnreadAndMentions = (serverUrl: string, observer: ({ return subscription; }; -export const subscribeMentionsByServer = (serverUrl: string, observer: (serverUrl: string, {myChannels, threadMentionCount}: ObserverArgs) => void) => { +export const subscribeMentionsByServer = (serverUrl: string, observer: ServerUnreadObserver) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; @@ -55,7 +67,7 @@ export const subscribeMentionsByServer = (serverUrl: string, observer: (serverUr return subscription; }; -export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer: (serverUrl: string, {myChannels, threadMentionCount}: ObserverArgs) => void) => { +export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer: ServerUnreadObserver) => { const server = DatabaseManager.serverDatabases[serverUrl]; let subscription: Subscription|undefined; @@ -64,8 +76,9 @@ export const subscribeUnreadAndMentionsByServer = (serverUrl: string, observer: query(Q.on(CHANNEL, Q.where('delete_at', Q.eq(0)))). observeWithColumns(['mentions_count', 'is_unread']). pipe( + combineLatestWith(observeAllMyChannelNotifyProps(server.database)), combineLatestWith(observeThreadMentionCount(server.database, undefined, false)), - map$(([myChannels, threadMentionCount]) => ({myChannels, threadMentionCount})), + map$(([[myChannels, settings], threadMentionCount]) => ({myChannels, settings, threadMentionCount})), ). subscribe(observer.bind(undefined, serverUrl)); } diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 90c780ec0..07eb1815b 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -3,7 +3,7 @@ import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; import {of as of$, Observable} from 'rxjs'; -import {switchMap, distinctUntilChanged} from 'rxjs/operators'; +import {map as map$, switchMap, distinctUntilChanged} from 'rxjs/operators'; import {General, Permissions} from '@constants'; import {MM_TABLES} from '@constants/database'; @@ -367,6 +367,10 @@ export const observeChannelInfo = (database: Database, channelId: string) => { ); }; +export const queryAllMyChannelSettings = (database: Database) => { + return database.get(MY_CHANNEL_SETTINGS).query(); +}; + export const queryMyChannelSettingsByIds = (database: Database, ids: string[]) => { return database.get(MY_CHANNEL_SETTINGS). query( @@ -374,6 +378,15 @@ export const queryMyChannelSettingsByIds = (database: Database, ids: string[]) = ); }; +export const observeAllMyChannelNotifyProps = (database: Database) => { + return queryAllMyChannelSettings(database).observeWithColumns(['notify_props']).pipe( + map$((settings) => settings.reduce>>((obj, setting) => { + obj[setting.id] = setting.notifyProps; + return obj; + }, {})), + ); +}; + export const queryChannelsByNames = (database: Database, names: string[]) => { return database.get(CHANNEL).query(Q.where('name', Q.oneOf(names))); }; diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/index.ts b/app/screens/home/channel_list/categories_list/categories/unreads/index.ts index 4d3b20e53..0db6dfca8 100644 --- a/app/screens/home/channel_list/categories_list/categories/unreads/index.ts +++ b/app/screens/home/channel_list/categories_list/categories/unreads/index.ts @@ -4,11 +4,12 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$, combineLatest} from 'rxjs'; -import {concatAll, map, switchMap} from 'rxjs/operators'; +import {combineLatestWith, concatAll, map, switchMap} from 'rxjs/operators'; +import {MyChannelModel} from '@app/database/models/server'; import {Preferences} from '@constants'; import {getPreferenceAsBool} from '@helpers/api/preference'; -import {getChannelById, queryMyChannelUnreads} from '@queries/servers/channel'; +import {getChannelById, observeAllMyChannelNotifyProps, queryMyChannelUnreads} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {observeLastUnreadChannelId} from '@queries/servers/system'; @@ -31,6 +32,16 @@ const concatenateChannelsArray = ([a, b]: CA) => { return of$(b ? a.filter((c) => c && c.id !== b.id).concat(b) : a); }; +type NotifyProps = { + [key: string]: Partial; +} + +const filterMutedFromMyChannels = ([myChannels, notifyProps]: [MyChannelModel[], NotifyProps]) => { + return myChannels.filter( + (myChannel) => notifyProps[myChannel.id]?.mark_unread !== 'mention' || myChannel.mentionsCount > 0, // Muted with Mentions should still go through + ); +}; + const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}: WithDatabaseProps) => { const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS). observeWithColumns(['value']). @@ -45,8 +56,11 @@ const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}: const lastUnread = observeLastUnreadChannelId(database).pipe( switchMap(getC), ); + const notifyProps = observeAllMyChannelNotifyProps(database); const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe( + combineLatestWith(notifyProps), + map(filterMutedFromMyChannels), map(getChannelsFromRelation), concatAll(), ); diff --git a/app/screens/home/channel_list/servers/index.tsx b/app/screens/home/channel_list/servers/index.tsx index 566bff677..e9d038627 100644 --- a/app/screens/home/channel_list/servers/index.tsx +++ b/app/screens/home/channel_list/servers/index.tsx @@ -9,14 +9,13 @@ import ServerIcon from '@components/server_icon'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {subscribeAllServers} from '@database/subscription/servers'; -import {subscribeUnreadAndMentionsByServer} from '@database/subscription/unreads'; +import {subscribeUnreadAndMentionsByServer, UnreadObserverArgs} from '@database/subscription/unreads'; import {useIsTablet} from '@hooks/device'; import {bottomSheet} from '@screens/navigation'; import ServerList from './servers_list'; import type ServersModel from '@typings/database/models/app/servers'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type {UnreadMessages, UnreadSubscription} from '@typings/database/subscriptions'; const subscriptions: Map = new Map(); @@ -70,14 +69,15 @@ const Servers = React.forwardRef((props, ref) => { setTotal({mentions, unread}); }; - const unreadsSubscription = (serverUrl: string, {myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { + const unreadsSubscription = (serverUrl: string, {myChannels, settings, threadMentionCount}: UnreadObserverArgs) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; let unread = false; for (const myChannel of myChannels) { + const isMuted = settings?.[myChannel.id]?.mark_unread === 'mention'; mentions += myChannel.mentionsCount; - unread = unread || myChannel.isUnread; + unread = unread || (myChannel.isUnread && !isMuted); } unreads.mentions = mentions + threadMentionCount; 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 eb791a92d..9cd9ab8cc 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 @@ -20,7 +20,7 @@ import TutorialSwipeLeft from '@components/tutorial_highlight/swipe_left'; import {Events} from '@constants'; import {useTheme} from '@context/theme'; import DatabaseManager from '@database/manager'; -import {subscribeServerUnreadAndMentions} from '@database/subscription/unreads'; +import {subscribeServerUnreadAndMentions, UnreadObserverArgs} from '@database/subscription/unreads'; import {useIsTablet} from '@hooks/device'; import {dismissBottomSheet} from '@screens/navigation'; import {alertServerError, alertServerLogout, alertServerRemove, editServer, loginToServer} from '@utils/server'; @@ -32,7 +32,6 @@ import Options from './options'; import WebSocket from './websocket'; import type ServersModel from '@typings/database/models/app/servers'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type {Subscription} from 'rxjs'; type Props = { @@ -138,12 +137,13 @@ const ServerItem = ({highlight, isActive, server, tutorialWatched}: Props) => { displayName = intl.formatMessage({id: 'servers.default', defaultMessage: 'Default Server'}); } - const unreadsSubscription = ({myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { + const unreadsSubscription = ({myChannels, settings, threadMentionCount}: UnreadObserverArgs) => { let mentions = 0; let isUnread = false; for (const myChannel of myChannels) { + const isMuted = settings?.[myChannel.id]?.mark_unread === 'mention'; mentions += myChannel.mentionsCount; - isUnread = isUnread || myChannel.isUnread; + isUnread = isUnread || (myChannel.isUnread && !isMuted); } mentions += threadMentionCount; diff --git a/app/screens/home/tab_bar/home.tsx b/app/screens/home/tab_bar/home.tsx index d88a9b715..f74b00c1d 100644 --- a/app/screens/home/tab_bar/home.tsx +++ b/app/screens/home/tab_bar/home.tsx @@ -8,11 +8,10 @@ import Badge from '@components/badge'; import CompassIcon from '@components/compass_icon'; import {BOTTOM_TAB_ICON_SIZE} from '@constants/view'; import {subscribeAllServers} from '@database/subscription/servers'; -import {subscribeUnreadAndMentionsByServer} from '@database/subscription/unreads'; +import {subscribeUnreadAndMentionsByServer, UnreadObserverArgs} from '@database/subscription/unreads'; import {changeOpacity} from '@utils/theme'; import type ServersModel from '@typings/database/models/app/servers'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type {UnreadMessages, UnreadSubscription} from '@typings/database/subscriptions'; type Props = { @@ -51,14 +50,15 @@ const Home = ({isFocused, theme}: Props) => { setTotal({mentions, unread}); }; - const unreadsSubscription = (serverUrl: string, {myChannels, threadMentionCount}: {myChannels: MyChannelModel[]; threadMentionCount: number}) => { + const unreadsSubscription = (serverUrl: string, {myChannels, settings, threadMentionCount}: UnreadObserverArgs) => { const unreads = subscriptions.get(serverUrl); if (unreads) { let mentions = 0; let unread = false; for (const myChannel of myChannels) { + const isMuted = settings?.[myChannel.id]?.mark_unread === 'mention'; mentions += myChannel.mentionsCount; - unread = unread || myChannel.isUnread; + unread = unread || (myChannel.isUnread && !isMuted); } unreads.mentions = mentions + threadMentionCount;