diff --git a/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap b/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
index ae6f9a82cc..0ca9a6f667 100644
--- a/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
+++ b/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
@@ -2,129 +2,109 @@
exports[`components/channel_list/categories/body/channel_item should match snapshot 1`] = `
-
+
+
+
-
-
-
-
- Hello!
-
-
+ Hello!
+
@@ -133,129 +113,109 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
exports[`components/channel_list/categories/body/channel_item should match snapshot when it has a draft 1`] = `
-
+
+
+
-
-
-
-
- Hello!
-
-
+ Hello!
+
diff --git a/app/components/channel_item/channel_item.test.tsx b/app/components/channel_item/channel_item.test.tsx
index 592b68e9d2..b8b5a8f489 100644
--- a/app/components/channel_item/channel_item.test.tsx
+++ b/app/components/channel_item/channel_item.test.tsx
@@ -37,10 +37,8 @@ describe('components/channel_list/categories/body/channel_item', () => {
membersCount={0}
myChannel={myChannel}
isMuted={false}
- collapsed={false}
currentUserId={'id'}
testID='channel_list_item'
- isVisible={true}
onPress={() => undefined}
/>,
);
@@ -57,10 +55,8 @@ describe('components/channel_list/categories/body/channel_item', () => {
membersCount={3}
myChannel={myChannel}
isMuted={false}
- collapsed={false}
currentUserId={'id'}
testID='channel_list_item'
- isVisible={true}
onPress={() => undefined}
/>,
);
diff --git a/app/components/channel_item/channel_item.tsx b/app/components/channel_item/channel_item.tsx
index b832b3f91f..17c5296eb6 100644
--- a/app/components/channel_item/channel_item.tsx
+++ b/app/components/channel_item/channel_item.tsx
@@ -1,10 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback, useEffect, useMemo} from 'react';
+import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
-import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import Badge from '@components/badge';
import ChannelIcon from '@components/channel_icon';
@@ -22,13 +21,11 @@ import type MyChannelModel from '@typings/database/models/servers/my_channel';
type Props = {
channel: ChannelModel;
- collapsed: boolean;
currentUserId: string;
hasDraft: boolean;
isActive: boolean;
isInfo?: boolean;
isMuted: boolean;
- isVisible: boolean;
membersCount: number;
myChannel?: MyChannelModel;
onPress: (channelId: string) => void;
@@ -115,8 +112,8 @@ export const textStyle = StyleSheet.create({
});
const ChannelListItem = ({
- channel, collapsed, currentUserId, hasDraft,
- isActive, isInfo, isMuted, isVisible, membersCount,
+ channel, currentUserId, hasDraft,
+ isActive, isInfo, isMuted, membersCount,
myChannel, onPress, teamDisplayName, testID}: Props) => {
const {formatMessage} = useIntl();
const theme = useTheme();
@@ -126,8 +123,6 @@ const ChannelListItem = ({
// Make it brighter if it's not muted, and highlighted or has unreads
const isBright = !isMuted && (myChannel && (myChannel.isUnread || myChannel.mentionsCount > 0));
- const shouldCollapse = (collapsed && !isBright) && !isActive;
- const sharedValue = useSharedValue(shouldCollapse);
const height = useMemo(() => {
let h = 40;
if (isInfo) {
@@ -136,18 +131,6 @@ const ChannelListItem = ({
return h;
}, [teamDisplayName, isInfo, isTablet]);
- useEffect(() => {
- sharedValue.value = shouldCollapse;
- }, [shouldCollapse]);
-
- const animatedStyle = useAnimatedStyle(() => {
- return {
- marginVertical: withTiming(sharedValue.value ? 0 : 2, {duration: 500}),
- height: withTiming(sharedValue.value ? 0 : height, {duration: 500}),
- opacity: withTiming(sharedValue.value ? 0 : 1, {duration: 500, easing: Easing.inOut(Easing.exp)}),
- };
- }, [height]);
-
const handleOnPress = useCallback(() => {
onPress(myChannel?.id || channel.id);
}, [channel.id, myChannel?.id]);
@@ -169,7 +152,7 @@ const ChannelListItem = ({
],
[height, isActive, isInfo, styles]);
- if ((!isInfo && (channel.deleteAt > 0 && !isActive)) || !myChannel || !isVisible) {
+ if (!myChannel) {
return null;
}
@@ -182,37 +165,36 @@ const ChannelListItem = ({
}
return (
-
-
- <>
-
-
- 0}
- membersCount={membersCount}
- name={channel.name}
- shared={channel.shared}
- size={24}
- type={channel.type}
- isMuted={isMuted}
- />
-
-
- {displayName}
-
- {isInfo && Boolean(teamDisplayName) && !isTablet &&
+
+ <>
+
+
+ 0}
+ membersCount={membersCount}
+ name={channel.name}
+ shared={channel.shared}
+ size={24}
+ type={channel.type}
+ isMuted={isMuted}
+ />
+
+
+ {displayName}
+
+ {isInfo && Boolean(teamDisplayName) && !isTablet &&
{teamDisplayName}
- }
-
- {Boolean(teammateId) &&
-
- }
- {isInfo && Boolean(teamDisplayName) && isTablet &&
-
- {teamDisplayName}
-
}
- 0}
- value={myChannel.mentionsCount}
- style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
+ {Boolean(teammateId) &&
+
+ }
+ {isInfo && Boolean(teamDisplayName) && isTablet &&
+
+ {teamDisplayName}
+
+ }
- >
-
-
+ 0}
+ value={myChannel.mentionsCount}
+ style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
+ />
+
+ >
+
);
};
diff --git a/app/components/channel_item/index.ts b/app/components/channel_item/index.ts
index e9a877b6fb..da0ada9709 100644
--- a/app/components/channel_item/index.ts
+++ b/app/components/channel_item/index.ts
@@ -4,14 +4,12 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
-import {of as of$, combineLatest} from 'rxjs';
+import {of as of$} from 'rxjs';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
-import {General, Preferences} from '@constants';
-import {getPreferenceAsBool} from '@helpers/api/preference';
+import {General} from '@constants';
import {observeMyChannel} from '@queries/servers/channel';
import {queryDraft} from '@queries/servers/drafts';
-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';
@@ -19,18 +17,15 @@ import MyChannelModel from '@typings/database/models/servers/my_channel';
import ChannelItem from './channel_item';
import type {WithDatabaseArgs} from '@typings/database/database';
-import type PreferenceModel from '@typings/database/models/servers/preference';
type EnhanceProps = WithDatabaseArgs & {
channel: ChannelModel;
- isInfo?: boolean;
- isUnreads?: boolean;
showTeamName?: boolean;
}
const observeIsMutedSetting = (mc: MyChannelModel) => mc.settings.observe().pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')));
-const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({channel, database, isInfo, isUnreads, showTeamName}: EnhanceProps) => {
+const enhance = withObservables(['channel', 'showTeamName'], ({channel, database, showTeamName}: EnhanceProps) => {
const currentUserId = observeCurrentUserId(database);
const myChannel = observeMyChannel(database, channel.id);
@@ -39,29 +34,6 @@ const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({chan
);
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))),
- );
-
- const isVisible = combineLatest([myChannel, unreadsOnTop]).pipe(
- switchMap(([mc, u]) => {
- if (!mc) {
- return of$(false);
- }
-
- if (isInfo) {
- return of$(true);
- }
-
- if (isUnreads) {
- return of$(u);
- }
-
- return u ? of$(!mc.isUnread || !mc.mentionsCount) : of$(true);
- }),
- );
const isMuted = myChannel.pipe(
switchMap((mc) => {
@@ -90,7 +62,6 @@ const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({chan
hasDraft,
isActive,
isMuted,
- isVisible,
membersCount,
myChannel,
teamDisplayName,
diff --git a/app/screens/find_channels/filtered_list/filtered_list.tsx b/app/screens/find_channels/filtered_list/filtered_list.tsx
index 09c0d64c75..e1a997a704 100644
--- a/app/screens/find_channels/filtered_list/filtered_list.tsx
+++ b/app/screens/find_channels/filtered_list/filtered_list.tsx
@@ -170,7 +170,6 @@ const FilteredList = ({
}, {displayName}),
);
return;
- return;
}
await close();
@@ -209,7 +208,6 @@ const FilteredList = ({
return (
-
-
-
-
-
-
-
-
-
-
- Channel
-
-
-
-
-
-
-
- ,
- ],
- "props": Object {
- "data": Anything,
- "getItem": [Function],
- "getItemCount": [Function],
- "initialNumToRender": 20,
- "invertStickyHeaders": undefined,
- "keyExtractor": [Function],
- "onContentSizeChange": [Function],
- "onLayout": [Function],
- "onMomentumScrollBegin": [Function],
- "onMomentumScrollEnd": [Function],
- "onScroll": [Function],
- "onScrollBeginDrag": [Function],
- "onScrollEndDrag": [Function],
- "removeClippedSubviews": true,
- "renderItem": [Function],
- "scrollEventThrottle": 50,
- "stickyHeaderIndices": Array [],
- "style": undefined,
- "updateCellsBatchingPeriod": 10,
- "viewabilityConfigCallbackPairs": Array [],
- "windowSize": 15,
- },
- "type": "RCTScrollView",
-}
-`;
diff --git a/app/screens/home/channel_list/categories_list/categories/body/category_body.test.tsx b/app/screens/home/channel_list/categories_list/categories/body/category_body.test.tsx
index 2e7b3d3e62..6030c78aad 100644
--- a/app/screens/home/channel_list/categories_list/categories/body/category_body.test.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/body/category_body.test.tsx
@@ -35,15 +35,14 @@ describe('components/channel_list/categories/body', () => {
undefined}
/>,
{database},
);
setTimeout(() => {
- expect(wrapper.toJSON()).toMatchSnapshot({
- props: {data: expect.anything()},
- });
+ expect(wrapper.toJSON()).toBeTruthy();
done();
});
});
diff --git a/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx b/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
index bcf68333f2..02abea899d 100644
--- a/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
@@ -1,8 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback, useMemo} from 'react';
+import React, {useCallback, useEffect, useMemo} from 'react';
import {FlatList} from 'react-native';
+import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import ChannelItem from '@components/channel_item';
import {DMS_CATEGORY} from '@constants/categories';
@@ -39,26 +40,40 @@ const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit, onChan
return (
);
- }, [category.collapsed, onChannelSwitch]);
+ }, [onChannelSwitch]);
+
+ const sharedValue = useSharedValue(category.collapsed);
+
+ useEffect(() => {
+ sharedValue.value = category.collapsed;
+ }, [category.collapsed]);
+
+ const height = ids.length ? ids.length * 40 : 0;
+
+ const animatedStyle = useAnimatedStyle(() => {
+ return {
+ height: withTiming(sharedValue.value ? 1 : height, {duration: 300}),
+ opacity: withTiming(sharedValue.value ? 0 : 1, {duration: sharedValue.value ? 200 : 300, easing: Easing.inOut(Easing.exp)}),
+ };
+ }, [height]);
return (
-
+
+ // @ts-expect-error strictMode not exposed on the types
+ strictMode={true}
+ />
+
);
};
diff --git a/app/screens/home/channel_list/categories_list/categories/body/index.ts b/app/screens/home/channel_list/categories_list/categories/body/index.ts
index 09d45f5c8e..0186cab2be 100644
--- a/app/screens/home/channel_list/categories_list/categories/body/index.ts
+++ b/app/screens/home/channel_list/categories_list/categories/body/index.ts
@@ -5,13 +5,14 @@ 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 {map, switchMap, concatAll} from 'rxjs/operators';
+import {map, switchMap, concatAll, combineLatestWith} from 'rxjs/operators';
import {General, Preferences} from '@constants';
import {DMS_CATEGORY} from '@constants/categories';
-import {queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
+import {getPreferenceAsBool} from '@helpers/api/preference';
+import {observeAllMyChannelNotifyProps, queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
-import {observeCurrentUserId} from '@queries/servers/system';
+import {observeCurrentChannelId, observeCurrentUserId, observeLastUnreadChannelId} from '@queries/servers/system';
import {WithDatabaseArgs} from '@typings/database/database';
import {getDirectChannelName} from '@utils/channel';
@@ -104,11 +105,12 @@ type EnhanceProps = {
category: CategoryModel;
locale: string;
currentUserId: string;
+ isTablet: boolean;
} & WithDatabaseArgs
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
-const enhance = withObservables(['category', 'locale'], ({category, locale, database, currentUserId}: EnhanceProps) => {
+const enhance = withObservables(['category', 'isTablet', 'locale'], ({category, locale, isTablet, database, currentUserId}: EnhanceProps) => {
const observedCategory = category.observe();
const sortedChannels = observedCategory.pipe(
switchMap((c) => getSortedChannels(database, c, locale)),
@@ -145,10 +147,42 @@ const enhance = withObservables(['category', 'locale'], ({category, locale, data
([a, b]) => of$(new Set(a.concat(b))),
));
+ 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 notifyProps = observeAllMyChannelNotifyProps(database);
+ const lastUnreadId = isTablet ? observeLastUnreadChannelId(database) : of$(undefined);
+ const unreadChannelIds = category.myChannels.observeWithColumns(['mentions_count', 'is_unread']).pipe(
+ combineLatestWith(unreadsOnTop, notifyProps, lastUnreadId),
+ map(([my, unreadTop, settings, lastUnread]) => {
+ if (!unreadTop) {
+ return new Set();
+ }
+ return my.reduce>((set, m) => {
+ const isMuted = settings[m.id]?.mark_unread === 'mention';
+ if ((isMuted && m.mentionsCount) || (!isMuted && m.isUnread) || m.id === lastUnread) {
+ set.add(m.id);
+ }
+ return set;
+ }, new Set());
+ }),
+ );
+
+ const currentChannelId = observeCurrentChannelId(database);
+ const filtered = sortedChannels.pipe(
+ combineLatestWith(currentChannelId, unreadChannelIds),
+ map(([channels, ccId, unreadIds]) => {
+ return channels.filter((c) => c && ((c.deleteAt > 0 && c.id === ccId) || !c.deleteAt) && !unreadIds.has(c.id));
+ }),
+ );
+
return {
limit,
hiddenChannelIds,
- sortedChannels,
+ sortedChannels: filtered,
category: observedCategory,
};
});
diff --git a/app/screens/home/channel_list/categories_list/categories/categories.tsx b/app/screens/home/channel_list/categories_list/categories/categories.tsx
index e20d7a821c..9a278233d1 100644
--- a/app/screens/home/channel_list/categories_list/categories/categories.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/categories.tsx
@@ -7,6 +7,7 @@ import {FlatList, StyleSheet} from 'react-native';
import {switchToChannelById} from '@actions/remote/channel';
import {useServerUrl} from '@context/server';
+import {useIsTablet} from '@hooks/device';
import CategoryBody from './body';
import LoadCategoriesError from './error';
@@ -35,6 +36,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
const intl = useIntl();
const listRef = useRef(null);
const serverUrl = useServerUrl();
+ const isTablet = useIsTablet();
const onChannelSwitch = useCallback(async (channelId: string) => {
switchToChannelById(serverUrl, channelId);
@@ -45,6 +47,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
return (
);
@@ -54,12 +57,13 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
>
);
- }, [currentTeamId, intl.locale, onChannelSwitch]);
+ }, [currentTeamId, intl.locale, isTablet, onChannelSwitch]);
useEffect(() => {
listRef.current?.scrollToOffset({animated: false, offset: 0});
@@ -87,11 +91,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
keyExtractor={extractKey}
- removeClippedSubviews={true}
- initialNumToRender={5}
- windowSize={15}
- updateCellsBatchingPeriod={10}
- maxToRenderPerBatch={5}
+ initialNumToRender={categoriesToShow.length}
// @ts-expect-error strictMode not included in the types
strictMode={true}
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 0db6dfca8e..a5e48c687f 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
@@ -21,7 +21,10 @@ 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 WithDatabaseProps = WithDatabaseArgs & {
+ currentTeamId: string;
+ isTablet: boolean;
+}
type CA = [
a: Array,
@@ -42,7 +45,7 @@ const filterMutedFromMyChannels = ([myChannels, notifyProps]: [MyChannelModel[],
);
};
-const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}: WithDatabaseProps) => {
+const enhanced = withObservables(['currentTeamId', 'isTablet'], ({currentTeamId, isTablet, database}: WithDatabaseProps) => {
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
observeWithColumns(['value']).
pipe(
@@ -53,9 +56,9 @@ const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}:
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
if (gU) {
- const lastUnread = observeLastUnreadChannelId(database).pipe(
+ const lastUnread = isTablet ? observeLastUnreadChannelId(database).pipe(
switchMap(getC),
- );
+ ) : of$('');
const notifyProps = observeAllMyChannelNotifyProps(database);
const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe(
diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
index 564f692f2f..2b9d652411 100644
--- a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
@@ -36,8 +36,6 @@ const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesPro
return (
);