diff --git a/app/actions/local/channel.ts b/app/actions/local/channel.ts
index 1539070fd..f17731175 100644
--- a/app/actions/local/channel.ts
+++ b/app/actions/local/channel.ts
@@ -6,6 +6,7 @@ import {DeviceEventEmitter} from 'react-native';
import {General, Navigation as NavigationConstants, Preferences, Screens} from '@constants';
import {CHANNELS_CATEGORY, DMS_CATEGORY} from '@constants/categories';
+import {SYSTEM_IDENTIFIERS} from '@constants/database';
import DatabaseManager from '@database/manager';
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
import {extractChannelDisplayName} from '@helpers/database';
@@ -467,3 +468,18 @@ export async function addChannelToDefaultCategory(serverUrl: string, channel: Ch
return {models};
}
+
+export async function showUnreadChannelsOnly(serverUrl: string, onlyUnreads: boolean) {
+ const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
+ if (!operator) {
+ return {error: `${serverUrl} database not found`};
+ }
+
+ return operator.handleSystem({
+ systems: [{
+ id: SYSTEM_IDENTIFIERS.ONLY_UNREADS,
+ value: JSON.stringify(onlyUnreads),
+ }],
+ prepareRecordsOnly: false,
+ });
+}
diff --git a/app/constants/database.ts b/app/constants/database.ts
index 0fdca2fd4..040a483af 100644
--- a/app/constants/database.ts
+++ b/app/constants/database.ts
@@ -54,6 +54,7 @@ export const SYSTEM_IDENTIFIERS = {
DATA_RETENTION_POLICIES: 'dataRetentionPolicies',
EXPANDED_LINKS: 'expandedLinks',
LICENSE: 'license',
+ ONLY_UNREADS: 'onlyUnreads',
PUSH_VERIFICATION_STATUS: 'pushVerificationStatus',
RECENT_CUSTOM_STATUS: 'recentCustomStatus',
RECENT_MENTIONS: 'recentMentions',
diff --git a/app/queries/servers/system.ts b/app/queries/servers/system.ts
index a934b0770..e8b2ecc5f 100644
--- a/app/queries/servers/system.ts
+++ b/app/queries/servers/system.ts
@@ -398,3 +398,10 @@ export const getLastUnreadChannelId = async (serverDatabase: Database): Promise<
return '';
}
};
+
+export const observeOnlyUnreads = (database: Database) => {
+ return querySystemValue(database, SYSTEM_IDENTIFIERS.ONLY_UNREADS).observeWithColumns(['value']).pipe(
+ switchMap((result) => (result.length ? result[0].observe() : of$({value: false}))),
+ switchMap((model) => of$(model.value as boolean)),
+ );
+};
diff --git a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
index 5c8055160..a74370738 100644
--- a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
+++ b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
@@ -415,53 +415,112 @@ exports[`components/categories_list should render team error 1`] = `
-
-
- Find channels...
-
+
+
+
+
+
+
+
+ Find channels...
+
+
(item === 'UNREADS' ? 'UNREADS' : item.id);
-const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
+const Categories = ({categories, currentTeamId, onlyUnreads, unreadsOnTop}: Props) => {
const intl = useIntl();
const listRef = useRef(null);
const serverUrl = useServerUrl();
@@ -49,6 +50,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
currentTeamId={currentTeamId}
isTablet={isTablet}
onChannelSwitch={onChannelSwitch}
+ onlyUnreads={onlyUnreads}
/>
);
}
@@ -63,20 +65,23 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
/>
>
);
- }, [currentTeamId, intl.locale, isTablet, onChannelSwitch]);
+ }, [currentTeamId, intl.locale, isTablet, onChannelSwitch, onlyUnreads]);
useEffect(() => {
listRef.current?.scrollToOffset({animated: false, offset: 0});
}, [currentTeamId]);
const categoriesToShow = useMemo(() => {
+ if (onlyUnreads && !unreadsOnTop) {
+ return ['UNREADS' as const];
+ }
const orderedCategories = [...categories];
orderedCategories.sort((a, b) => a.sortOrder - b.sortOrder);
if (unreadsOnTop) {
return ['UNREADS' as const, ...orderedCategories];
}
return orderedCategories;
- }, [categories, unreadsOnTop]);
+ }, [categories, onlyUnreads, unreadsOnTop]);
if (!categories.length) {
return ;
diff --git a/app/screens/home/channel_list/categories_list/categories/index.ts b/app/screens/home/channel_list/categories_list/categories/index.ts
index 11cadbb7b..0161f676c 100644
--- a/app/screens/home/channel_list/categories_list/categories/index.ts
+++ b/app/screens/home/channel_list/categories_list/categories/index.ts
@@ -10,6 +10,7 @@ import {Preferences} from '@constants';
import {getPreferenceAsBool} from '@helpers/api/preference';
import {queryCategoriesByTeamIds} from '@queries/servers/categories';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
+import {observeOnlyUnreads} from '@queries/servers/system';
import Categories from './categories';
@@ -31,6 +32,7 @@ const enhanced = withObservables(
return {
categories,
+ onlyUnreads: observeOnlyUnreads(database),
unreadsOnTop,
};
});
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 4e1c5336e..efe1f65b5 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
@@ -24,6 +24,7 @@ import type PreferenceModel from '@typings/database/models/servers/preference';
type WithDatabaseProps = WithDatabaseArgs & {
currentTeamId: string;
isTablet: boolean;
+ onlyUnreads: boolean;
}
type CA = [
@@ -89,7 +90,7 @@ const filterAndSortMyChannels = ([myChannels, notifyProps]: [MyChannelModel[], N
return [...mentions, ...unreads, ...mutedMentions];
};
-const enhanced = withObservables(['currentTeamId', 'isTablet'], ({currentTeamId, isTablet, database}: WithDatabaseProps) => {
+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']).
pipe(
@@ -99,7 +100,7 @@ const enhanced = withObservables(['currentTeamId', 'isTablet'], ({currentTeamId,
const getC = (lastUnreadChannelId: string) => getChannelById(database, lastUnreadChannelId);
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
- if (gU) {
+ if (gU || onlyUnreads) {
const lastUnread = isTablet ? observeLastUnreadChannelId(database).pipe(
switchMap(getC),
) : of$('');
diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx
index f0159f90e..6bfe9904d 100644
--- a/app/screens/home/channel_list/categories_list/index.tsx
+++ b/app/screens/home/channel_list/categories_list/index.tsx
@@ -12,7 +12,7 @@ import Categories from './categories';
import ChannelListHeader from './header';
import LoadChannelsError from './load_channels_error';
import LoadTeamsError from './load_teams_error';
-import SearchField from './search';
+import SubHeader from './subheader';
import ThreadsButton from './threads_button';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@@ -68,7 +68,7 @@ const CategoriesList = ({channelsCount, currentTeamId, iconPad, isCRTEnabled, is
} else {
content = (
<>
-
+
{isCRTEnabled && }
{
+ 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))),
+ );
+
+ return {
+ unreadsOnTop,
+ };
+});
+
+export default withDatabase(enhanced(SubHeader));
diff --git a/app/screens/home/channel_list/categories_list/search/__snapshots__/index.test.tsx.snap b/app/screens/home/channel_list/categories_list/subheader/search_field/__snapshots__/index.test.tsx.snap
similarity index 97%
rename from app/screens/home/channel_list/categories_list/search/__snapshots__/index.test.tsx.snap
rename to app/screens/home/channel_list/categories_list/subheader/search_field/__snapshots__/index.test.tsx.snap
index d26b4a3b3..8b7d36ac3 100644
--- a/app/screens/home/channel_list/categories_list/search/__snapshots__/index.test.tsx.snap
+++ b/app/screens/home/channel_list/categories_list/subheader/search_field/__snapshots__/index.test.tsx.snap
@@ -15,12 +15,12 @@ exports[`Search Field should match snapshot 1`] = `
Object {
"backgroundColor": "rgba(255,255,255,0.12)",
"borderRadius": 8,
+ "flex": 1,
"flexDirection": "row",
"height": 40,
"justifyContent": "flex-start",
"marginVertical": 20,
"padding": 8,
- "width": "100%",
}
}
>
diff --git a/app/screens/home/channel_list/categories_list/search/index.test.tsx b/app/screens/home/channel_list/categories_list/subheader/search_field/index.test.tsx
similarity index 100%
rename from app/screens/home/channel_list/categories_list/search/index.test.tsx
rename to app/screens/home/channel_list/categories_list/subheader/search_field/index.test.tsx
diff --git a/app/screens/home/channel_list/categories_list/search/index.tsx b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx
similarity index 99%
rename from app/screens/home/channel_list/categories_list/search/index.tsx
rename to app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx
index ae21ec9be..4e86bf841 100644
--- a/app/screens/home/channel_list/categories_list/search/index.tsx
+++ b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx
@@ -18,7 +18,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
flexDirection: 'row',
justifyContent: 'flex-start',
- width: '100%',
+ flex: 1,
backgroundColor: changeOpacity(theme.sidebarText, 0.12),
borderRadius: 8,
padding: 8,
diff --git a/app/screens/home/channel_list/categories_list/subheader/subheader.tsx b/app/screens/home/channel_list/categories_list/subheader/subheader.tsx
new file mode 100644
index 000000000..46a6d9ffc
--- /dev/null
+++ b/app/screens/home/channel_list/categories_list/subheader/subheader.tsx
@@ -0,0 +1,44 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useEffect} from 'react';
+import {StyleSheet, View} from 'react-native';
+import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+
+import SearchField from './search_field';
+import UnreadFilter from './unread_filter';
+
+type Props = {
+ unreadsOnTop: boolean;
+}
+
+const style = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ },
+});
+
+const SubHeader = ({unreadsOnTop}: Props) => {
+ const showFilter = useSharedValue(!unreadsOnTop);
+
+ const animatedStyle = useAnimatedStyle(() => ({
+ marginRight: withTiming(showFilter.value ? 8 : 0, {duration: 300}),
+ width: withTiming(showFilter.value ? 40 : 0, {duration: 300}),
+ opacity: withTiming(showFilter.value ? 1 : 0, {duration: 300}),
+ }));
+
+ useEffect(() => {
+ showFilter.value = !unreadsOnTop;
+ }, [unreadsOnTop]);
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default SubHeader;
diff --git a/app/screens/home/channel_list/categories_list/subheader/unread_filter/index.ts b/app/screens/home/channel_list/categories_list/subheader/unread_filter/index.ts
new file mode 100644
index 000000000..c69c62453
--- /dev/null
+++ b/app/screens/home/channel_list/categories_list/subheader/unread_filter/index.ts
@@ -0,0 +1,17 @@
+// 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 {observeOnlyUnreads} from '@queries/servers/system';
+
+import UnreadFilter from './unread_filter';
+
+import type {WithDatabaseArgs} from '@typings/database/database';
+
+const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
+ onlyUnreads: observeOnlyUnreads(database),
+}));
+
+export default withDatabase(enhanced(UnreadFilter));
diff --git a/app/screens/home/channel_list/categories_list/subheader/unread_filter/unread_filter.tsx b/app/screens/home/channel_list/categories_list/subheader/unread_filter/unread_filter.tsx
new file mode 100644
index 000000000..c7ccd5512
--- /dev/null
+++ b/app/screens/home/channel_list/categories_list/subheader/unread_filter/unread_filter.tsx
@@ -0,0 +1,54 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {TouchableWithoutFeedback, View} from 'react-native';
+
+import {showUnreadChannelsOnly} from '@actions/local/channel';
+import CompassIcon from '@components/compass_icon';
+import {useServerUrl} from '@context/server';
+import {useTheme} from '@context/theme';
+import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+
+type Props = {
+ onlyUnreads: boolean;
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
+ container: {
+ alignItems: 'center',
+ backgroundColor: changeOpacity(theme.sidebarText, 0.12),
+ borderRadius: 8,
+ height: 40,
+ justifyContent: 'center',
+ marginVertical: 20,
+ width: 40,
+ },
+ filtered: {
+ backgroundColor: theme.sidebarText,
+ },
+}));
+
+const UnreadFilter = ({onlyUnreads}: Props) => {
+ const theme = useTheme();
+ const serverUrl = useServerUrl();
+ const styles = getStyleSheet(theme);
+
+ const onPress = () => {
+ showUnreadChannelsOnly(serverUrl, !onlyUnreads);
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default UnreadFilter;