diff --git a/app/components/channel_icon/index.tsx b/app/components/channel_icon/index.tsx
index 3d37f8f303..04aaed4eb4 100644
--- a/app/components/channel_icon/index.tsx
+++ b/app/components/channel_icon/index.tsx
@@ -196,4 +196,4 @@ const ChannelIcon = ({
);
};
-export default ChannelIcon;
+export default React.memo(ChannelIcon);
diff --git a/app/components/channel_list/categories/body/__snapshots__/category_body.test.tsx.snap b/app/components/channel_list/categories/body/__snapshots__/category_body.test.tsx.snap
index ebb430c566..0a23da2e35 100644
--- a/app/components/channel_list/categories/body/__snapshots__/category_body.test.tsx.snap
+++ b/app/components/channel_list/categories/body/__snapshots__/category_body.test.tsx.snap
@@ -27,97 +27,93 @@ Object {
}
}
>
-
-
-
-
-
- Channel
-
+ testID="undefined.public"
+ />
+
+ Channel
+
-
+
,
diff --git a/app/components/channel_list/categories/body/category_body.test.tsx b/app/components/channel_list/categories/body/category_body.test.tsx
index 3a32e3bf20..cd356efbb8 100644
--- a/app/components/channel_list/categories/body/category_body.test.tsx
+++ b/app/components/channel_list/categories/body/category_body.test.tsx
@@ -30,7 +30,7 @@ describe('components/channel_list/categories/body', () => {
category = categories[0];
});
- it('should match snapshot', () => {
+ it('should match snapshot', (done) => {
const wrapper = renderWithEverything(
{
/>,
{database},
);
- expect(wrapper.toJSON()).toMatchSnapshot({
- props: {data: expect.anything()},
+
+ setTimeout(() => {
+ expect(wrapper.toJSON()).toMatchSnapshot({
+ props: {data: expect.anything()},
+ });
+ done();
});
});
});
diff --git a/app/components/channel_list/categories/body/category_body.tsx b/app/components/channel_list/categories/body/category_body.tsx
index 2961cf21fe..ac7fb2f739 100644
--- a/app/components/channel_list/categories/body/category_body.tsx
+++ b/app/components/channel_list/categories/body/category_body.tsx
@@ -4,40 +4,42 @@
import React, {useCallback, useMemo} from 'react';
import {FlatList} from 'react-native';
+import ChannelModel from '@typings/database/models/servers/channel';
+
import ChannelListItem from './channel';
import type CategoryModel from '@typings/database/models/servers/category';
type Props = {
currentChannelId: string;
- sortedIds: string[];
+ sortedChannels: ChannelModel[];
hiddenChannelIds: string[];
category: CategoryModel;
limit: number;
};
-const extractKey = (item: string) => item;
+const extractKey = (item: ChannelModel) => item.id;
-const CategoryBody = ({currentChannelId, sortedIds, category, hiddenChannelIds, limit}: Props) => {
+const CategoryBody = ({currentChannelId, sortedChannels, category, hiddenChannelIds, limit}: Props) => {
const ids = useMemo(() => {
- let filteredIds = sortedIds;
+ let filteredChannels = sortedChannels;
// Remove all closed gm/dms
if (hiddenChannelIds.length) {
- filteredIds = sortedIds.filter((id) => !hiddenChannelIds.includes(id));
+ filteredChannels = sortedChannels.filter((item) => item && !hiddenChannelIds.includes(item.id));
}
if (category.type === 'direct_messages' && limit > 0) {
- return filteredIds.slice(0, limit - 1);
+ return filteredChannels.slice(0, limit - 1);
}
- return filteredIds;
- }, [category.type, limit, hiddenChannelIds]);
+ return filteredChannels;
+ }, [category.type, limit, hiddenChannelIds, sortedChannels]);
- const ChannelItem = useCallback(({item}: {item: string}) => {
+ const ChannelItem = useCallback(({item}: {item: ChannelModel}) => {
return (
);
diff --git a/app/components/channel_list/categories/body/channel/__snapshots__/channel_list_item.test.tsx.snap b/app/components/channel_list/categories/body/channel/__snapshots__/channel_list_item.test.tsx.snap
index 21c182dc8e..3a0156ef24 100644
--- a/app/components/channel_list/categories/body/channel/__snapshots__/channel_list_item.test.tsx.snap
+++ b/app/components/channel_list/categories/body/channel/__snapshots__/channel_list_item.test.tsx.snap
@@ -20,34 +20,47 @@ exports[`components/channel_list/categories/body/channel/item should match snaps
}
}
>
-
-
-
- 1
-
-
+ 1
+
-
- Hello!
-
+
+ Hello!
+
-
+
`;
diff --git a/app/components/channel_list/categories/body/channel/channel_list_item.test.tsx b/app/components/channel_list/categories/body/channel/channel_list_item.test.tsx
index 38e2972845..e6a1764f22 100644
--- a/app/components/channel_list/categories/body/channel/channel_list_item.test.tsx
+++ b/app/components/channel_list/categories/body/channel/channel_list_item.test.tsx
@@ -10,6 +10,7 @@ import TestHelper from '@test/test_helper';
import ChannelListItem from './channel_list_item';
+import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
describe('components/channel_list/categories/body/channel/item', () => {
@@ -30,12 +31,12 @@ describe('components/channel_list/categories/body/channel/item', () => {
it('should match snapshot', () => {
const wrapper = renderWithIntlAndTheme(
,
);
diff --git a/app/components/channel_list/categories/body/channel/channel_list_item.tsx b/app/components/channel_list/categories/body/channel/channel_list_item.tsx
index 230fc9fc29..4642418299 100644
--- a/app/components/channel_list/categories/body/channel/channel_list_item.tsx
+++ b/app/components/channel_list/categories/body/channel/channel_list_item.tsx
@@ -1,10 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useEffect, useMemo} from 'react';
+import React, {useCallback, useEffect, useMemo} from 'react';
import {useIntl} from 'react-intl';
-import {StyleSheet, Text, View} from 'react-native';
-import {TouchableOpacity} from 'react-native-gesture-handler';
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import {switchToChannelById} from '@actions/remote/channel';
@@ -15,6 +14,7 @@ import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
+import {getUserIdFromChannelName} from '@utils/user';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
@@ -61,20 +61,22 @@ const textStyle = StyleSheet.create({
});
type Props = {
- channel: Pick;
+ channel: ChannelModel;
isActive: boolean;
- isOwnDirectMessage: boolean;
isMuted: boolean;
myChannel?: MyChannelModel;
collapsed: boolean;
+ currentUserId: string;
}
-const ChannelListItem = ({channel, isActive, isOwnDirectMessage, isMuted, myChannel, collapsed}: Props) => {
+const ChannelListItem = ({channel, isActive, currentUserId, isMuted, myChannel, collapsed}: Props) => {
const {formatMessage} = useIntl();
const theme = useTheme();
const styles = getStyleSheet(theme);
const serverUrl = useServerUrl();
+ const isOwnDirectMessage = (channel.type === General.DM_CHANNEL) && currentUserId === getUserIdFromChannelName(currentUserId, channel.name);
+
// Make it brighter if it's not muted, and highlighted or has unreads
const bright = !isMuted && (isActive || (myChannel && (myChannel.isUnread || myChannel.mentionsCount > 0)));
@@ -82,7 +84,7 @@ const ChannelListItem = ({channel, isActive, isOwnDirectMessage, isMuted, myChan
useEffect(() => {
sharedValue.value = collapsed && !bright;
- }, [collapsed, bright]);
+ }, [collapsed && !bright]);
const animatedStyle = useAnimatedStyle(() => {
return {
@@ -92,14 +94,14 @@ const ChannelListItem = ({channel, isActive, isOwnDirectMessage, isMuted, myChan
};
});
- const switchChannels = () => {
+ const switchChannels = useCallback(() => {
if (myChannel) {
switchToChannelById(serverUrl, myChannel.id);
}
- };
+ }, [myChannel?.id, serverUrl]);
const membersCount = useMemo(() => {
if (channel.type === General.GM_CHANNEL) {
- return channel.displayName?.split(',').length;
+ return channel.displayName.split(',').length;
}
return 0;
}, [channel.type, channel.displayName]);
diff --git a/app/components/channel_list/categories/body/channel/index.ts b/app/components/channel_list/categories/body/channel/index.ts
index 4aeca82c5f..13301b2be1 100644
--- a/app/components/channel_list/categories/body/channel/index.ts
+++ b/app/components/channel_list/categories/body/channel/index.ts
@@ -3,50 +3,30 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
-import {combineLatest, of as of$} from 'rxjs';
+import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
-import {General} from '@constants';
import {observeMyChannel} from '@queries/servers/channel';
import {observeCurrentUserId} from '@queries/servers/system';
-import {getUserIdFromChannelName} from '@utils/user';
+import ChannelModel from '@typings/database/models/servers/channel';
import ChannelListItem from './channel_list_item';
import type {WithDatabaseArgs} from '@typings/database/database';
-const enhance = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => {
- const myChannel = observeMyChannel(database, channelId);
+const enhance = withObservables(['channel'], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => {
+ const myChannel = observeMyChannel(database, channel.id);
const currentUserId = observeCurrentUserId(database);
- const channel = myChannel.pipe(switchMap((my) => (my ? my.channel.observe() : of$(undefined))));
- const settings = channel.pipe(switchMap((c) => (c ? c.settings.observe() : of$(undefined))));
+ const settings = channel.settings.observe();
- const isOwnDirectMessage = combineLatest([currentUserId, channel]).pipe(
- switchMap(([userId, ch]) => {
- if (ch?.type === General.DM_CHANNEL) {
- const teammateId = getUserIdFromChannelName(userId, ch.name);
- return of$(userId === teammateId);
- }
-
- return of$(false);
- }),
- );
return {
- isOwnDirectMessage,
+ currentUserId,
isMuted: settings.pipe(
switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')),
),
myChannel,
- channel: channel.pipe(
- switchMap((c) => of$({
- deleteAt: c?.deleteAt || 0,
- displayName: c?.displayName || '',
- name: c?.name || '',
- shared: c?.shared || false,
- type: c?.type || '',
- })),
- ),
+ channel: channel.observe(),
};
});
diff --git a/app/components/channel_list/categories/body/index.ts b/app/components/channel_list/categories/body/index.ts
index 578e21a8b6..dc94303f3c 100644
--- a/app/components/channel_list/categories/body/index.ts
+++ b/app/components/channel_list/categories/body/index.ts
@@ -5,7 +5,7 @@ import {Database} from '@nozbe/watermelondb';
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {combineLatest, of as of$} from 'rxjs';
-import {switchMap} from 'rxjs/operators';
+import {map, switchMap, concatAll} from 'rxjs/operators';
import {General, Preferences} from '@constants';
import {queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
@@ -16,7 +16,9 @@ import {getDirectChannelName} from '@utils/channel';
import CategoryBody from './category_body';
import type CategoryModel from '@typings/database/models/servers/category';
+import type CategoryChannelModel from '@typings/database/models/servers/category_channel';
import type ChannelModel from '@typings/database/models/servers/channel';
+import type MyChannelModel from '@typings/database/models/servers/my_channel';
import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings';
import type PreferenceModel from '@typings/database/models/servers/preference';
@@ -45,8 +47,7 @@ const buildAlphaData = (channels: ChannelModel[], settings: MyChannelSettingsMod
});
combined.sort(sortAlpha.bind(null, locale));
-
- return of$(combined.map((c) => c.id));
+ return of$(combined.map((cdata) => channels.find((c) => c.id === cdata.id)));
};
const observeSettings = (database: Database, channels: ChannelModel[]) => {
@@ -54,7 +55,11 @@ const observeSettings = (database: Database, channels: ChannelModel[]) => {
return queryMyChannelSettingsByIds(database, ids).observeWithColumns(['notify_props']);
};
-const getSortedIds = (database: Database, category: CategoryModel, locale: string) => {
+const getChannelsFromRelation = async (relations: CategoryChannelModel[] | MyChannelModel[]) => {
+ return Promise.all(relations.map((r) => r.channel?.fetch()));
+};
+
+const getSortedChannels = (database: Database, category: CategoryModel, locale: string) => {
switch (category.sorting) {
case 'alpha': {
const channels = category.channels.observeWithColumns(['display_name']);
@@ -67,14 +72,14 @@ const getSortedIds = (database: Database, category: CategoryModel, locale: strin
}
case 'manual': {
return category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']).pipe(
- // eslint-disable-next-line max-nested-callbacks
- switchMap((cc) => of$(cc.map((c) => c.channelId))),
+ map(getChannelsFromRelation),
+ concatAll(),
);
}
default:
return category.myChannels.observeWithColumns(['last_post_at']).pipe(
- // eslint-disable-next-line max-nested-callbacks
- switchMap((mc) => of$(mc.map((m) => m.id))),
+ map(getChannelsFromRelation),
+ concatAll(),
);
}
};
@@ -87,8 +92,8 @@ type EnhanceProps = {category: CategoryModel; locale: string; currentUserId: str
const enhance = withObservables(['category'], ({category, locale, database, currentUserId}: EnhanceProps) => {
const observedCategory = category.observe();
- const sortedIds = observedCategory.pipe(
- switchMap((c) => getSortedIds(database, c, locale)),
+ const sortedChannels = observedCategory.pipe(
+ switchMap((c) => getSortedChannels(database, c, locale)),
);
const dmMap = (p: PreferenceModel) => getDirectChannelName(p.name, currentUserId);
@@ -124,7 +129,7 @@ const enhance = withObservables(['category'], ({category, locale, database, curr
return {
limit,
hiddenChannelIds,
- sortedIds,
+ sortedChannels,
category: observedCategory,
};
});