diff --git a/app/helpers/database/index.ts b/app/helpers/database/index.ts index 737b31f4ca..59aefa0280 100644 --- a/app/helpers/database/index.ts +++ b/app/helpers/database/index.ts @@ -5,6 +5,24 @@ import {General} from '@constants'; import type Model from '@nozbe/watermelondb/Model'; import type ChannelModel from '@typings/database/models/servers/channel'; +import type MyChannelModel from '@typings/database/models/servers/my_channel'; + +type NotifyProps = { + [key: string]: Partial; +} + +/** + * Filtering / Sorting: + * + * Unreads, Mentions, and Muted Mentions Only + * Mentions on top, then unreads, then muted channels with mentions. + */ + + type FilterAndSortMyChannelsArgs = [ + MyChannelModel[], + Record, + NotifyProps, +] export const extractRecordsForTable = (records: Model[], tableName: string): T[] => { // @ts-expect-error constructor.table not exposed in type definition @@ -32,3 +50,44 @@ export function extractChannelDisplayName(raw: Pick { + return channels.reduce>((result, c) => { + result[c.id] = c; + return result; + }, {}); +}; + +export const filterAndSortMyChannels = ([myChannels, channels, notifyProps]: FilterAndSortMyChannelsArgs): ChannelModel[] => { + const mentions: ChannelModel[] = []; + const unreads: ChannelModel[] = []; + const mutedMentions: ChannelModel[] = []; + + const isMuted = (id: string) => { + return notifyProps[id]?.mark_unread === 'mention'; + }; + + for (const myChannel of myChannels) { + const id = myChannel.id; + + // is it a mention? + if (!isMuted(id) && myChannel.mentionsCount > 0 && channels[id]) { + mentions.push(channels[id]); + continue; + } + + // is it unread? + if (!isMuted(myChannel.id) && myChannel.isUnread && channels[id]) { + unreads.push(channels[id]); + continue; + } + + // is it a muted mention? + if (isMuted(myChannel.id) && myChannel.mentionsCount > 0 && channels[id]) { + mutedMentions.push(channels[id]); + continue; + } + } + + return [...mentions, ...unreads, ...mutedMentions]; +}; diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index e081383d31..dd3d35af1b 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -466,16 +466,21 @@ export function observeMyChannelMentionCount(database: Database, teamId?: string export function queryMyChannelsByUnread(database: Database, isUnread: boolean, sortBy: 'last_viewed_at' | 'last_post_at', take: number, excludeIds?: string[]) { const clause: Q.Clause[] = [Q.where('is_unread', Q.eq(isUnread))]; + const count: Q.Clause[] = []; if (excludeIds?.length) { clause.push(Q.where('id', Q.notIn(excludeIds))); } + if (take > 0) { + count.push(Q.take(take)); + } + return queryAllMyChannel(database).extend( Q.on(CHANNEL, Q.where('delete_at', Q.eq(0))), ...clause, Q.sortBy(sortBy, Q.desc), - Q.take(take), + ...count, ); } diff --git a/app/screens/find_channels/unfiltered_list/index.ts b/app/screens/find_channels/unfiltered_list/index.ts index a5925f1092..4fcbcdf3f7 100644 --- a/app/screens/find_channels/unfiltered_list/index.ts +++ b/app/screens/find_channels/unfiltered_list/index.ts @@ -5,9 +5,10 @@ import {Database} from '@nozbe/watermelondb'; 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, switchMap} from 'rxjs/operators'; -import {queryMyChannelsByUnread} from '@queries/servers/channel'; +import {filterAndSortMyChannels, makeChannelsMap} from '@helpers/database'; +import {observeNotifyPropsByChannels, queryMyChannelsByUnread} from '@queries/servers/channel'; import {queryJoinedTeams} from '@queries/servers/team'; import {retrieveChannels} from '@screens/find_channels/utils'; @@ -30,10 +31,18 @@ const observeRecentChannels = (database: Database, unreads: ChannelModel[]) => { const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const teamsCount = queryJoinedTeams(database).observeCount(); - const unreadChannels = queryMyChannelsByUnread(database, true, 'last_post_at', MAX_UNREAD_CHANNELS). - observeWithColumns(['last_post_at']).pipe( - switchMap((myChannels) => retrieveChannels(database, myChannels)), - ); + const myUnreadChannels = queryMyChannelsByUnread(database, true, 'last_post_at', 0). + observeWithColumns(['last_post_at']); + const notifyProps = myUnreadChannels.pipe(switchMap((cs) => observeNotifyPropsByChannels(database, cs))); + const channels = myUnreadChannels.pipe( + switchMap((myChannels) => retrieveChannels(database, myChannels)), + ); + const channelsMap = channels.pipe(switchMap((cs) => of$(makeChannelsMap(cs)))); + const unreadChannels = myUnreadChannels.pipe( + combineLatestWith(channelsMap, notifyProps), + map(filterAndSortMyChannels), + switchMap((cs) => of$(cs.slice(0, MAX_UNREAD_CHANNELS))), + ); const recentChannels = unreadChannels.pipe( switchMap((unreads) => observeRecentChannels(database, unreads)), 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 4c2e51c202..bd470a14bb 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 @@ -8,6 +8,7 @@ import {combineLatestWith, map, switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {filterAndSortMyChannels, makeChannelsMap} from '@helpers/database'; import {getChannelById, observeChannelsByLastPostAt, observeNotifyPropsByChannels, queryMyChannelUnreads} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {observeLastUnreadChannelId} from '@queries/servers/system'; @@ -16,7 +17,6 @@ import UnreadCategories from './unreads'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type PreferenceModel from '@typings/database/models/servers/preference'; type WithDatabaseProps = WithDatabaseArgs & { @@ -30,68 +30,10 @@ type CA = [ b: ChannelModel | undefined, ] -type NotifyProps = { - [key: string]: Partial; -} - -/** - * Filtering / Sorting: - * - * Unreads, Mentions, and Muted Mentions Only - * Mentions on top, then unreads, then muted channels with mentions. - */ - - type FilterAndSortMyChannelsArgs = [ - MyChannelModel[], - Record, - NotifyProps, -] - const concatenateChannelsArray = ([a, b]: CA) => { return of$(b ? a.filter((c) => c && c.id !== b.id).concat(b) : a); }; -const filterAndSortMyChannels = ([myChannels, channels, notifyProps]: FilterAndSortMyChannelsArgs): ChannelModel[] => { - const mentions: ChannelModel[] = []; - const unreads: ChannelModel[] = []; - const mutedMentions: ChannelModel[] = []; - - const isMuted = (id: string) => { - return notifyProps[id]?.mark_unread === 'mention'; - }; - - for (const myChannel of myChannels) { - const id = myChannel.id; - - // is it a mention? - if (!isMuted(id) && myChannel.mentionsCount > 0 && channels[id]) { - mentions.push(channels[id]); - continue; - } - - // is it unread? - if (!isMuted(myChannel.id) && myChannel.isUnread && channels[id]) { - unreads.push(channels[id]); - continue; - } - - // is it a muted mention? - if (isMuted(myChannel.id) && myChannel.mentionsCount > 0 && channels[id]) { - mutedMentions.push(channels[id]); - continue; - } - } - - return [...mentions, ...unreads, ...mutedMentions]; -}; - -const makeChannelsMap = (channels: ChannelModel[]) => { - return channels.reduce>((result, c) => { - result[c.id] = c; - return result; - }, {}); -}; - const enhanced = withObservables(['currentTeamId', 'isTablet', 'onlyUnreads'], ({currentTeamId, isTablet, database, onlyUnreads}: WithDatabaseProps) => { const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS). observeWithColumns(['value']). diff --git a/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx index 14fddec86d..2e8a840595 100644 --- a/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx +++ b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx @@ -9,6 +9,7 @@ import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import {useTheme} from '@context/theme'; import {findChannels} from '@screens/navigation'; +import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -41,12 +42,12 @@ const SearchField = () => { const intl = useIntl(); const styles = getStyleSheet(theme); - const onPress = useCallback(() => { + const onPress = useCallback(preventDoubleTap(() => { findChannels( intl.formatMessage({id: 'find_channels.title', defaultMessage: 'Find Channels'}), theme, ); - }, [intl.locale, theme]); + }), [intl.locale, theme]); return (