Performance improvements in the area of home and channel switch. (#6174)
* Performance improvements * Revert hidden DM/GMs changes * Minor fixes related to the revert * Add observeWithColumns missed in merge
This commit is contained in:
parent
667716fd0f
commit
7a90f2d4a8
14 changed files with 173 additions and 132 deletions
|
|
@ -7,7 +7,6 @@ import {IntlShape} from 'react-intl';
|
|||
|
||||
import {storeCategories} from '@actions/local/category';
|
||||
import {addChannelToDefaultCategory, storeMyChannelsForTeam, switchToChannel} from '@actions/local/channel';
|
||||
import EphemeralStore from '@app/store/ephemeral_store';
|
||||
import {General, Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {privateChannelJoinPrompt} from '@helpers/api/channel';
|
||||
|
|
@ -18,6 +17,7 @@ import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
|||
import {getCommonSystemValues, getCurrentTeamId, getCurrentUserId} from '@queries/servers/system';
|
||||
import {prepareMyTeams, getNthLastChannelFromTeam, getMyTeamById, getTeamById, getTeamByName} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {generateChannelNameFromDisplayName, getDirectChannelName, isDMorGM} from '@utils/channel';
|
||||
import {PERMALINK_GENERIC_TEAM_NAME_REDIRECT} from '@utils/url';
|
||||
import {displayGroupMessageName, displayUsername} from '@utils/user';
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import {fetchMissingSidebarInfo, fetchMyChannel, fetchChannelStats, fetchChannel
|
|||
import {fetchPostsForChannel} from '@actions/remote/post';
|
||||
import {fetchRolesIfNeeded} from '@actions/remote/role';
|
||||
import {fetchUsersByIds, updateUsersNoLongerVisible} from '@actions/remote/user';
|
||||
import EphemeralStore from '@app/store/ephemeral_store';
|
||||
import Events from '@constants/events';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {queryActiveServer} from '@queries/app/servers';
|
||||
|
|
@ -26,6 +25,7 @@ import {prepareCommonSystemValues, getConfig, setCurrentChannelId, getCurrentCha
|
|||
import {getNthLastChannelFromTeam} from '@queries/servers/team';
|
||||
import {getCurrentUser, getTeammateNameDisplay, getUserById} from '@queries/servers/user';
|
||||
import {dismissAllModals, popToRoot} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
|
||||
// Received when current user created a channel in a different client
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@ describe('components/channel_list/categories/body', () => {
|
|||
<CategoryBody
|
||||
category={category}
|
||||
locale={DEFAULT_LOCALE}
|
||||
currentChannelId={''}
|
||||
currentUserId={''}
|
||||
unreadChannelIds={new Set()}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import ChannelListItem from './channel';
|
|||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
type Props = {
|
||||
currentChannelId: string;
|
||||
sortedChannels: ChannelModel[];
|
||||
hiddenChannelIds: Set<string>;
|
||||
category: CategoryModel;
|
||||
|
|
@ -21,7 +20,7 @@ type Props = {
|
|||
|
||||
const extractKey = (item: ChannelModel) => item.id;
|
||||
|
||||
const CategoryBody = ({currentChannelId, sortedChannels, category, hiddenChannelIds, limit}: Props) => {
|
||||
const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit}: Props) => {
|
||||
const ids = useMemo(() => {
|
||||
let filteredChannels = sortedChannels;
|
||||
|
||||
|
|
@ -36,26 +35,28 @@ const CategoryBody = ({currentChannelId, sortedChannels, category, hiddenChannel
|
|||
return filteredChannels;
|
||||
}, [category.type, limit, hiddenChannelIds, sortedChannels]);
|
||||
|
||||
const ChannelItem = useCallback(({item}: {item: ChannelModel}) => {
|
||||
const renderItem = useCallback(({item}: {item: ChannelModel}) => {
|
||||
return (
|
||||
<ChannelListItem
|
||||
channel={item}
|
||||
isActive={item.id === currentChannelId}
|
||||
collapsed={category.collapsed}
|
||||
testID={`category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_list_item`}
|
||||
/>
|
||||
);
|
||||
}, [currentChannelId, category.collapsed]);
|
||||
}, [category.collapsed]);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={ids}
|
||||
renderItem={ChannelItem}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={extractKey}
|
||||
removeClippedSubviews={true}
|
||||
initialNumToRender={20}
|
||||
windowSize={15}
|
||||
updateCellsBatchingPeriod={10}
|
||||
|
||||
// @ts-expect-error strictMode not exposed on the types
|
||||
strictMode={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ describe('components/channel_list/categories/body/channel/item', () => {
|
|||
collapsed={false}
|
||||
currentUserId={'id'}
|
||||
testID='channel_list_item'
|
||||
isVisible={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,11 @@ type Props = {
|
|||
myChannel?: MyChannelModel;
|
||||
collapsed: boolean;
|
||||
currentUserId: string;
|
||||
isVisible: boolean;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const ChannelListItem = ({channel, isActive, currentUserId, isMuted, myChannel, collapsed, testID}: Props) => {
|
||||
const ChannelListItem = ({channel, isActive, currentUserId, isMuted, isVisible, myChannel, collapsed, testID}: Props) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
|
@ -132,7 +133,7 @@ const ChannelListItem = ({channel, isActive, currentUserId, isMuted, myChannel,
|
|||
displayName = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
|
||||
}
|
||||
|
||||
if ((channel.deleteAt > 0 && !isActive) || !myChannel) {
|
||||
if ((channel.deleteAt > 0 && !isActive) || !myChannel || !isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,41 +3,66 @@
|
|||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import React from 'react';
|
||||
import {of as of$, combineLatest} from 'rxjs';
|
||||
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {observeMyChannel} from '@queries/servers/channel';
|
||||
import {observeCurrentUserId} from '@queries/servers/system';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
|
||||
import ChannelModel from '@typings/database/models/servers/channel';
|
||||
import MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
import ChannelListItem from './channel_list_item';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
|
||||
const enhance = withObservables(['channel'], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => {
|
||||
const observeIsMutedSetting = (mc: MyChannelModel) => mc.settings.observe().pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')));
|
||||
|
||||
const enhance = withObservables(['channel', 'isUnreads'], ({channel, isUnreads, database}: {channel: ChannelModel; isUnreads?: boolean} & WithDatabaseArgs) => {
|
||||
const currentUserId = observeCurrentUserId(database);
|
||||
|
||||
const myChannel = observeMyChannel(database, channel.id);
|
||||
|
||||
let isMuted = of$(false);
|
||||
if (myChannel) {
|
||||
const settings = myChannel.pipe(
|
||||
switchMap((mc) => {
|
||||
return mc ? mc.settings.observe() : of$(undefined);
|
||||
}),
|
||||
const isActive = observeCurrentChannelId(database).pipe(switchMap((id) => of$(id ? id === channel.id : false)), distinctUntilChanged());
|
||||
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||
);
|
||||
|
||||
isMuted = settings?.pipe(
|
||||
switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')),
|
||||
);
|
||||
}
|
||||
const isVisible = combineLatest([myChannel, unreadsOnTop]).pipe(
|
||||
switchMap(([mc, u]) => {
|
||||
if (!mc) {
|
||||
return of$(false);
|
||||
}
|
||||
if (isUnreads) {
|
||||
return of$(u);
|
||||
}
|
||||
|
||||
return u ? of$(!mc.isUnread) : of$(true);
|
||||
}),
|
||||
);
|
||||
|
||||
const isMuted = myChannel.pipe(
|
||||
switchMap((mc) => {
|
||||
if (!mc) {
|
||||
return of$(false);
|
||||
}
|
||||
return observeIsMutedSetting(mc);
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
currentUserId,
|
||||
isMuted,
|
||||
isActive,
|
||||
isVisible,
|
||||
myChannel,
|
||||
channel: channel.observe(),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhance(ChannelListItem));
|
||||
export default React.memo(withDatabase(enhance(ChannelListItem)));
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {General, Preferences} from '@constants';
|
|||
import {DMS_CATEGORY} from '@constants/categories';
|
||||
import {queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentUserId} from '@queries/servers/system';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
import {getDirectChannelName} from '@utils/channel';
|
||||
|
||||
|
|
@ -70,12 +71,10 @@ export const getChannelsFromRelation = async (relations: CategoryChannelModel[]
|
|||
return Promise.all(relations.map((r) => r.channel?.fetch()));
|
||||
};
|
||||
|
||||
const getSortedChannels = (database: Database, category: CategoryModel, unreadChannelIds: Set<string>, locale: string) => {
|
||||
const getSortedChannels = (database: Database, category: CategoryModel, locale: string) => {
|
||||
switch (category.sorting) {
|
||||
case 'alpha': {
|
||||
const channels = category.channels.observeWithColumns(['display_name']).pipe(
|
||||
map((cs) => cs.filter((c) => !unreadChannelIds.has(c.id))),
|
||||
);
|
||||
const channels = category.channels.observeWithColumns(['display_name']);
|
||||
const settings = channels.pipe(
|
||||
switchMap((cs) => observeSettings(database, cs)),
|
||||
);
|
||||
|
|
@ -85,14 +84,12 @@ const getSortedChannels = (database: Database, category: CategoryModel, unreadCh
|
|||
}
|
||||
case 'manual': {
|
||||
return category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']).pipe(
|
||||
map((cc) => cc.filter((c) => !unreadChannelIds.has(c.channelId))),
|
||||
map(getChannelsFromRelation),
|
||||
concatAll(),
|
||||
);
|
||||
}
|
||||
default:
|
||||
return category.myChannels.observeWithColumns(['last_post_at']).pipe(
|
||||
map((myCs) => myCs.filter((myC) => !unreadChannelIds.has(myC.id))),
|
||||
map(getChannelsFromRelation),
|
||||
concatAll(),
|
||||
);
|
||||
|
|
@ -107,13 +104,14 @@ type EnhanceProps = {
|
|||
category: CategoryModel;
|
||||
locale: string;
|
||||
currentUserId: string;
|
||||
unreadChannelIds: Set<string>;
|
||||
} & WithDatabaseArgs
|
||||
|
||||
const enhance = withObservables(['category', 'locale', 'unreadChannelIds'], ({category, locale, database, currentUserId, unreadChannelIds}: EnhanceProps) => {
|
||||
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
|
||||
|
||||
const enhance = withObservables(['category', 'locale'], ({category, locale, database, currentUserId}: EnhanceProps) => {
|
||||
const observedCategory = category.observe();
|
||||
const sortedChannels = observedCategory.pipe(
|
||||
switchMap((c) => getSortedChannels(database, c, unreadChannelIds, locale)),
|
||||
switchMap((c) => getSortedChannels(database, c, locale)),
|
||||
);
|
||||
|
||||
const dmMap = (p: PreferenceModel) => getDirectChannelName(p.name, currentUserId);
|
||||
|
|
@ -154,4 +152,4 @@ const enhance = withObservables(['category', 'locale', 'unreadChannelIds'], ({ca
|
|||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhance(CategoryBody));
|
||||
export default withDatabase(withUserId(enhance(CategoryBody)));
|
||||
|
|
|
|||
|
|
@ -11,14 +11,11 @@ import CategoryHeader from './header';
|
|||
import UnreadCategories from './unreads';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
|
||||
type Props = {
|
||||
categories: CategoryModel[];
|
||||
unreadChannels: ChannelModel[];
|
||||
currentChannelId: string;
|
||||
currentUserId: string;
|
||||
currentTeamId: string;
|
||||
unreadsOnTop: boolean;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
|
@ -29,51 +26,39 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
const extractKey = (item: CategoryModel) => (Array.isArray(item) ? 'UNREADS' : item.id);
|
||||
const extractKey = (item: CategoryModel | 'UNREADS') => (item === 'UNREADS' ? 'UNREADS' : item.id);
|
||||
|
||||
const Categories = ({categories, currentChannelId, currentUserId, currentTeamId, unreadChannels}: Props) => {
|
||||
const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
|
||||
const intl = useIntl();
|
||||
const listRef = useRef<FlatList>(null);
|
||||
|
||||
const unreadChannelIds = useMemo(() => new Set(unreadChannels.map((myC) => myC.id)), [unreadChannels]);
|
||||
const categoriesToDisplay: Array<CategoryModel|string[]> = useMemo(() => {
|
||||
if (unreadChannelIds.size) {
|
||||
return [Array.from(unreadChannelIds), ...categories];
|
||||
const renderCategory = useCallback((data: {item: CategoryModel | 'UNREADS'}) => {
|
||||
if (data.item === 'UNREADS') {
|
||||
return <UnreadCategories currentTeamId={currentTeamId}/>;
|
||||
}
|
||||
|
||||
return categories;
|
||||
}, [categories, unreadChannelIds]);
|
||||
|
||||
const renderCategory = useCallback((data: {item: CategoryModel | string[]}) => {
|
||||
if (Array.isArray(data.item)) {
|
||||
return (
|
||||
<UnreadCategories
|
||||
currentChannelId={currentChannelId}
|
||||
unreadChannels={unreadChannels}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CategoryHeader category={data.item}/>
|
||||
<CategoryBody
|
||||
category={data.item}
|
||||
currentChannelId={currentChannelId}
|
||||
currentUserId={currentUserId}
|
||||
locale={intl.locale}
|
||||
unreadChannelIds={unreadChannelIds}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}, [categories, currentChannelId, intl.locale, unreadChannels]);
|
||||
}, [intl.locale]);
|
||||
|
||||
useEffect(() => {
|
||||
listRef.current?.scrollToOffset({animated: false, offset: 0});
|
||||
}, [currentTeamId]);
|
||||
|
||||
// Sort Categories
|
||||
categories.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
const categoriesToShow = useMemo(() => {
|
||||
const orderedCategories = [...categories];
|
||||
orderedCategories.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
if (unreadsOnTop) {
|
||||
return ['UNREADS' as const, ...orderedCategories];
|
||||
}
|
||||
return orderedCategories;
|
||||
}, [categories, unreadsOnTop]);
|
||||
|
||||
if (!categories.length) {
|
||||
return <LoadCategoriesError/>;
|
||||
|
|
@ -81,7 +66,7 @@ const Categories = ({categories, currentChannelId, currentUserId, currentTeamId,
|
|||
|
||||
return (
|
||||
<FlatList
|
||||
data={categoriesToDisplay}
|
||||
data={categoriesToShow}
|
||||
ref={listRef}
|
||||
renderItem={renderCategory}
|
||||
style={styles.mainList}
|
||||
|
|
@ -93,6 +78,9 @@ const Categories = ({categories, currentChannelId, currentUserId, currentTeamId,
|
|||
windowSize={15}
|
||||
updateCellsBatchingPeriod={10}
|
||||
maxToRenderPerBatch={5}
|
||||
|
||||
// @ts-expect-error strictMode not included in the types
|
||||
strictMode={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,38 +3,24 @@
|
|||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {concatAll, map, switchMap} from 'rxjs/operators';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@app/constants';
|
||||
import {getPreferenceAsBool} from '@app/helpers/api/preference';
|
||||
import {getChannelById, queryMyChannelUnreads} from '@app/queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@app/queries/servers/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryCategoriesByTeamIds} from '@queries/servers/categories';
|
||||
import {observeCurrentChannelId, observeCurrentUserId, observeLastUnreadChannelId} from '@queries/servers/system';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
|
||||
import {getChannelsFromRelation} from './body';
|
||||
import Categories from './categories';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
|
||||
type CA = [
|
||||
a: Array<ChannelModel | null>,
|
||||
b: ChannelModel | undefined,
|
||||
]
|
||||
const concatenateChannelsArray = ([a, b]: CA) => {
|
||||
return of$(b ? a.filter((c) => c && c.id !== b.id).concat(b) : a);
|
||||
};
|
||||
|
||||
type WithDatabaseProps = { currentTeamId: string } & WithDatabaseArgs
|
||||
|
||||
const enhanced = withObservables(
|
||||
['currentTeamId'],
|
||||
({currentTeamId, database}: WithDatabaseProps) => {
|
||||
const currentChannelId = observeCurrentChannelId(database);
|
||||
const currentUserId = observeCurrentUserId(database);
|
||||
const categories = queryCategoriesByTeamIds(database, [currentTeamId]).observeWithColumns(['sort_order']);
|
||||
|
||||
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
|
|
@ -43,33 +29,9 @@ const enhanced = withObservables(
|
|||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||
);
|
||||
|
||||
const getC = (lastUnreadChannelId: string) => getChannelById(database, lastUnreadChannelId);
|
||||
|
||||
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
|
||||
if (gU) {
|
||||
const lastUnread = observeLastUnreadChannelId(database).pipe(
|
||||
switchMap(getC),
|
||||
);
|
||||
|
||||
const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe(
|
||||
map(getChannelsFromRelation),
|
||||
concatAll(),
|
||||
);
|
||||
|
||||
const combined = combineLatest([unreads, lastUnread]).pipe(
|
||||
switchMap(concatenateChannelsArray),
|
||||
);
|
||||
|
||||
return combined;
|
||||
}
|
||||
return of$([]);
|
||||
}));
|
||||
|
||||
return {
|
||||
unreadChannels,
|
||||
categories,
|
||||
currentUserId,
|
||||
currentChannelId,
|
||||
unreadsOnTop,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
67
app/components/channel_list/categories/unreads/index.ts
Normal file
67
app/components/channel_list/categories/unreads/index.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// 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 {of as of$, combineLatest} from 'rxjs';
|
||||
import {concatAll, map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {getChannelById, queryMyChannelUnreads} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeLastUnreadChannelId} from '@queries/servers/system';
|
||||
|
||||
import {getChannelsFromRelation} from '../body';
|
||||
|
||||
import UnreadCategories from './unreads';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
|
||||
type WithDatabaseProps = { currentTeamId: string } & WithDatabaseArgs
|
||||
|
||||
type CA = [
|
||||
a: Array<ChannelModel | null>,
|
||||
b: ChannelModel | undefined,
|
||||
]
|
||||
|
||||
const concatenateChannelsArray = ([a, b]: CA) => {
|
||||
return of$(b ? a.filter((c) => c && c.id !== b.id).concat(b) : a);
|
||||
};
|
||||
|
||||
const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}: WithDatabaseProps) => {
|
||||
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||
);
|
||||
|
||||
const getC = (lastUnreadChannelId: string) => getChannelById(database, lastUnreadChannelId);
|
||||
|
||||
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
|
||||
if (gU) {
|
||||
const lastUnread = observeLastUnreadChannelId(database).pipe(
|
||||
switchMap(getC),
|
||||
);
|
||||
|
||||
const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe(
|
||||
map(getChannelsFromRelation),
|
||||
concatAll(),
|
||||
);
|
||||
|
||||
const combined = combineLatest([unreads, lastUnread]).pipe(
|
||||
switchMap(concatenateChannelsArray),
|
||||
);
|
||||
|
||||
return combined;
|
||||
}
|
||||
return of$([]);
|
||||
}));
|
||||
return {
|
||||
unreadChannels,
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(UnreadCategories));
|
||||
|
|
@ -17,15 +17,14 @@ describe('components/channel_list/categories/body', () => {
|
|||
database = server.database;
|
||||
});
|
||||
|
||||
it('render without error', () => {
|
||||
it('do not render when there are no unread channels', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<UnreadsCategory
|
||||
currentChannelId={''}
|
||||
unreadChannels={[]}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toBeTruthy();
|
||||
expect(wrapper.toJSON()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {FlatList, Text} from 'react-native';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@app/utils/theme';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import ChannelListItem from './body/channel';
|
||||
import ChannelListItem from '../body/channel';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
|
||||
|
|
@ -23,26 +23,27 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const renderItem = ({item}: {item: ChannelModel}) => {
|
||||
return (
|
||||
<ChannelListItem
|
||||
channel={item}
|
||||
collapsed={false}
|
||||
isUnreads={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
type UnreadCategoriesProps = {
|
||||
unreadChannels: ChannelModel[];
|
||||
currentChannelId: string;
|
||||
}
|
||||
|
||||
const UnreadCategories = ({unreadChannels, currentChannelId}: UnreadCategoriesProps) => {
|
||||
const UnreadCategories = ({unreadChannels}: UnreadCategoriesProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const intl = useIntl();
|
||||
|
||||
const renderItem = useCallback(({item}: {item: ChannelModel}) => {
|
||||
return (
|
||||
<ChannelListItem
|
||||
channel={item}
|
||||
isActive={item.id === currentChannelId}
|
||||
collapsed={false}
|
||||
/>
|
||||
);
|
||||
}, [currentChannelId]);
|
||||
|
||||
if (!unreadChannels.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Text
|
||||
1
index.ts
1
index.ts
|
|
@ -17,6 +17,7 @@ import WebsocketManager from './app/init/websocket_manager';
|
|||
import {registerScreens} from './app/screens';
|
||||
import EphemeralStore from './app/store/ephemeral_store';
|
||||
import setFontFamily from './app/utils/font_family';
|
||||
import './app/utils/emoji'; // Imported to ensure it is loaded when used
|
||||
|
||||
declare const global: { HermesInternal: null | {} };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue