diff --git a/app/utils/buttonStyles.test.ts b/app/utils/buttonStyles.test.ts
new file mode 100644
index 000000000..80836cc78
--- /dev/null
+++ b/app/utils/buttonStyles.test.ts
@@ -0,0 +1,142 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import Preferences from '@constants/preferences';
+
+import {buttonBackgroundStyle, buttonStyles, getBackgroundStyles, buttonSizeStyles, buttonTextStyle, buttonTextStyles, buttonTextSizeStyles} from './buttonStyles';
+import {changeOpacity} from './theme';
+
+describe('get the style of a button', () => {
+ const defaultStyle = buttonStyles.main;
+ const theme = Preferences.THEMES.denim;
+ const bgStyles = getBackgroundStyles(theme);
+
+ test('button default values', () => {
+ const tests = [{
+ getStyle: () => buttonBackgroundStyle(theme),
+ expected: [
+ defaultStyle,
+ buttonSizeStyles.m,
+ bgStyles.primary.default.default,
+ ],
+ }, {
+ getStyle: () => buttonBackgroundStyle(theme, 'xs'),
+ expected: [
+ defaultStyle,
+ buttonSizeStyles.xs,
+ bgStyles.primary.default.default,
+ ],
+ }, {
+ getStyle: () => buttonBackgroundStyle(theme, 'lg', 'secondary'),
+ expected: [
+ defaultStyle,
+ buttonSizeStyles.lg,
+ bgStyles.secondary.default.default,
+ ],
+ }];
+
+ tests.forEach((t) => {
+ const style = t.getStyle();
+ expect(style).toEqual(t.expected);
+ });
+ });
+
+ test('button style', () => {
+ const btnEnphasis: ButtonEmphasis[] = ['primary', 'secondary', 'tertiary', 'link'];
+ const btnSizes: ButtonSize[] = ['lg', 'm', 's', 'xs'];
+ const btnTypes: ButtonType[] = ['default', 'destructive', 'disabled', 'inverted'];
+ const btnState: ButtonState[] = ['active', 'default', 'focus', 'hover'];
+
+ btnEnphasis.forEach((emphasis) => {
+ btnSizes.forEach((size) => {
+ btnTypes.forEach((type) => {
+ btnState.forEach((state) => {
+ const style = buttonBackgroundStyle(theme, size, emphasis, type, state);
+ expect(style).toEqual([
+ defaultStyle,
+ buttonSizeStyles[size],
+ bgStyles[emphasis][type][state],
+ ]);
+ });
+ });
+ });
+ });
+ });
+});
+
+describe('get the text style of a button', () => {
+ const defaultTextStyle = buttonTextStyles.main;
+ const theme = Preferences.THEMES.denim;
+
+ test('button text default values', () => {
+ const tests = [{
+ getStyle: () => buttonTextStyle(theme),
+ expected: [
+ defaultTextStyle,
+ buttonTextSizeStyles.m,
+ {color: theme.buttonColor},
+ ],
+ }, {
+ getStyle: () => buttonTextStyle(theme, 'xs'),
+ expected: [
+ defaultTextStyle,
+ buttonTextSizeStyles.xs,
+ {color: theme.buttonColor},
+ ],
+ }, {
+ getStyle: () => buttonTextStyle(theme, 'lg', 'secondary'),
+ expected: [
+ defaultTextStyle,
+ buttonTextSizeStyles.lg,
+ {color: theme.buttonBg},
+ ],
+ }];
+
+ tests.forEach((t) => {
+ const style = t.getStyle();
+ expect(style).toEqual(t.expected);
+ });
+ });
+
+ test('button style', () => {
+ const btnEnphasis: ButtonEmphasis[] = ['primary', 'secondary', 'tertiary', 'link'];
+ const btnSizes: ButtonSize[] = ['lg', 'm', 's', 'xs'];
+ const btnTypes: ButtonType[] = ['default', 'destructive', 'disabled', 'inverted'];
+
+ const getColor = (type: ButtonType, emphasis: ButtonEmphasis) => {
+ let color: string = theme.buttonColor;
+
+ if (type === 'disabled') {
+ color = changeOpacity(theme.centerChannelColor, 0.32);
+ }
+
+ if ((type === 'destructive' && emphasis !== 'primary')) {
+ color = theme.errorTextColor;
+ }
+
+ if ((type === 'inverted' && emphasis === 'primary') ||
+ (type !== 'inverted' && emphasis !== 'primary')) {
+ color = theme.buttonBg;
+ }
+
+ if (type === 'inverted' && emphasis === 'tertiary') {
+ color = theme.sidebarText;
+ }
+
+ return color;
+ };
+
+ btnEnphasis.forEach((emphasis) => {
+ btnSizes.forEach((size) => {
+ btnTypes.forEach((type) => {
+ const style = buttonTextStyle(theme, size, emphasis, type);
+ expect(style).toEqual([
+ defaultTextStyle,
+ buttonTextSizeStyles[size],
+ {color: getColor(type, emphasis)},
+ ]);
+ });
+ });
+ });
+ });
+});
diff --git a/app/utils/buttonStyles.ts b/app/utils/buttonStyles.ts
index e25049e62..823520e9e 100644
--- a/app/utils/buttonStyles.ts
+++ b/app/utils/buttonStyles.ts
@@ -5,37 +5,8 @@ import {type StyleProp, StyleSheet, type TextStyle, type ViewStyle} from 'react-
import {blendColors, changeOpacity} from '@utils/theme';
-/**
- * Returns the appropriate Style object for
- *
- *
- * @param theme
- * @param size
- * @param emphasis
- * @param type
- * @param state
- * @returns
- */
-export const buttonBackgroundStyle = (
- theme: Theme,
- size: ButtonSize = 'm',
- emphasis: ButtonEmphasis = 'primary',
- type: ButtonType = 'default',
- state: ButtonState = 'default',
-): StyleProp => {
- const styles = StyleSheet.create({
- main: {
- flex: 0,
- alignItems: 'center',
- justifyContent: 'center',
- borderRadius: 4,
- },
- fullWidth: {
- width: '100%',
- },
- });
-
- const backgroundStyles: BackgroundStyles = {
+export const getBackgroundStyles = (theme: Theme): BackgroundStyles => {
+ return {
primary: {
default: {
default: {
@@ -313,31 +284,102 @@ export const buttonBackgroundStyle = (
},
},
};
+};
- const sizes: ButtonSizes = StyleSheet.create({
- xs: {
- height: 24,
- paddingVertical: 6,
- paddingHorizontal: 10,
- },
- s: {
- height: 32,
- paddingVertical: 10,
- paddingHorizontal: 16,
- },
- m: {
- height: 40,
- paddingVertical: 12,
- paddingHorizontal: 20,
- },
- lg: {
- height: 48,
- paddingVertical: 14,
- paddingHorizontal: 24,
- },
- });
+export const buttonSizeStyles: ButtonSizes = StyleSheet.create({
+ xs: {
+ height: 24,
+ paddingVertical: 6,
+ paddingHorizontal: 10,
+ },
+ s: {
+ height: 32,
+ paddingVertical: 10,
+ paddingHorizontal: 16,
+ },
+ m: {
+ height: 40,
+ paddingVertical: 12,
+ paddingHorizontal: 20,
+ },
+ lg: {
+ height: 48,
+ paddingVertical: 14,
+ paddingHorizontal: 24,
+ },
+});
- return [styles.main, sizes[size], backgroundStyles[emphasis][type][state]];
+export const buttonStyles = StyleSheet.create({
+ main: {
+ flex: 0,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: 4,
+ },
+ fullWidth: {
+ width: '100%',
+ },
+});
+
+export const buttonTextStyles = StyleSheet.create({
+ main: {
+ fontFamily: 'OpenSans-SemiBold',
+ fontWeight: '600',
+ textAlignVertical: 'center',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 1,
+ },
+ underline: {
+ textDecorationLine: 'underline',
+ },
+});
+
+export const buttonTextSizeStyles = StyleSheet.create({
+ xs: {
+ fontSize: 11,
+ lineHeight: 10,
+ letterSpacing: 0.02,
+ marginTop: 2,
+ },
+ s: {
+ fontSize: 12,
+ lineHeight: 12,
+ marginTop: 1,
+ },
+ m: {
+ fontSize: 14,
+ lineHeight: 14,
+ marginTop: 3,
+ },
+ lg: {
+ fontSize: 16,
+ lineHeight: 18,
+ marginTop: 1,
+ },
+});
+
+/**
+ * Returns the appropriate Style object for
+ *
+ *
+ * @param theme
+ * @param size
+ * @param emphasis
+ * @param type
+ * @param state
+ * @returns
+ */
+export const buttonBackgroundStyle = (
+ theme: Theme,
+ size: ButtonSize = 'm',
+ emphasis: ButtonEmphasis = 'primary',
+ type: ButtonType = 'default',
+ state: ButtonState = 'default',
+): StyleProp => {
+ const backgroundStyles = getBackgroundStyles(theme);
+
+ return [buttonStyles.main, buttonSizeStyles[size], backgroundStyles[emphasis][type][state]];
};
/**
@@ -376,43 +418,5 @@ export const buttonTextStyle = (
color = theme.sidebarText;
}
- const styles = StyleSheet.create({
- main: {
- fontFamily: 'OpenSans-SemiBold',
- fontWeight: '600',
- textAlignVertical: 'center',
- alignItems: 'center',
- justifyContent: 'center',
- padding: 1,
- },
- underline: {
- textDecorationLine: 'underline',
- },
- });
-
- const sizes = StyleSheet.create({
- xs: {
- fontSize: 11,
- lineHeight: 10,
- letterSpacing: 0.02,
- marginTop: 2,
- },
- s: {
- fontSize: 12,
- lineHeight: 12,
- marginTop: 1,
- },
- m: {
- fontSize: 14,
- lineHeight: 14,
- marginTop: 3,
- },
- lg: {
- fontSize: 16,
- lineHeight: 18,
- marginTop: 1,
- },
- });
-
- return [styles.main, sizes[size], {color}];
+ return [buttonTextStyles.main, buttonTextSizeStyles[size], {color}];
};
diff --git a/app/utils/categories.test.ts b/app/utils/categories.test.ts
new file mode 100644
index 000000000..4a6bfd1b5
--- /dev/null
+++ b/app/utils/categories.test.ts
@@ -0,0 +1,663 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+/* eslint-disable max-lines */
+
+import {Preferences} from '@constants';
+import DatabaseManager from '@database/manager';
+
+import {
+ makeCategoryChannelId,
+ isUnreadChannel,
+ filterArchivedChannels,
+ filterAutoclosedDMs,
+ filterManuallyClosedDms,
+ sortChannels,
+ getUnreadIds,
+ type ChannelWithMyChannel,
+} from './categories';
+
+import type {ServerDatabase} from '@typings/database/database';
+import type MyChannelModel from '@typings/database/models/servers/my_channel';
+import type PreferenceModel from '@typings/database/models/servers/preference';
+import type UserModel from '@typings/database/models/servers/user';
+
+describe('Categories utils', () => {
+ const serverUrl = 'baseHandler.test.com';
+
+ const buildChannelsData = () => {
+ const dt1 = Date.now();
+ const dt2 = Date.now() - (24 * 60 * 60 * 1000);
+ const dt3 = Date.now() - (2 * 24 * 60 * 60 * 1000);
+ const dt4 = Date.now() - (3 * 24 * 60 * 60 * 1000);
+
+ const channels: Channel[] = [{
+ id: 'dm1',
+ name: 'me__other1',
+ display_name: 'DM 1',
+ type: 'D',
+ create_at: dt2,
+ update_at: dt2,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt2,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }, {
+ id: 'dm2',
+ name: 'me__other2',
+ display_name: 'Zandoval 2',
+ type: 'D',
+ create_at: dt4,
+ update_at: dt4,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt4,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }, {
+ id: 'open-channel',
+ name: 'open-channel',
+ display_name: 'Open Channel',
+ type: 'O',
+ create_at: dt1,
+ update_at: dt1,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt1,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }, {
+ id: 'gm1',
+ name: 'the_gm_channel_name',
+ display_name: 'Mario, Persona 2, Persona 3',
+ type: 'G',
+ create_at: dt3,
+ update_at: dt3,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt3,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }];
+ const myChannels: ChannelMembership[] = [
+ {
+ id: 'dm1',
+ user_id: 'me',
+ channel_id: 'dm1',
+ last_post_at: dt2,
+ last_viewed_at: dt2,
+ last_update_at: dt2,
+ mention_count: 0,
+ msg_count: 20,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ {
+ id: 'dm2',
+ user_id: 'me',
+ channel_id: 'dm2',
+ last_post_at: dt4,
+ last_viewed_at: dt4,
+ last_update_at: dt4,
+ mention_count: 3,
+ msg_count: 23,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ }, {
+ id: 'open-channel',
+ user_id: 'me',
+ channel_id: 'open-channel',
+ last_post_at: dt1,
+ last_viewed_at: dt1,
+ last_update_at: dt1,
+ mention_count: 1,
+ msg_count: 21,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'all',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ }, {
+ id: 'gm1',
+ user_id: 'me',
+ channel_id: 'gm1',
+ last_post_at: dt3,
+ last_viewed_at: dt3,
+ last_update_at: dt3,
+ mention_count: 0,
+ msg_count: 0,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ ];
+
+ return {channels, myChannels};
+ };
+
+ const createChannelsDbRecords = async (db: ServerDatabase, channels: Channel[], myChannels: ChannelMembership[]): Promise => {
+ const channelModels = await db.operator.handleChannel({channels, prepareRecordsOnly: false});
+ const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false});
+ return channelModels.map((channel, index) => {
+ const myChannel = myChannelModels[index];
+ return {
+ channel,
+ myChannel,
+ sortOrder: index,
+ };
+ });
+ };
+
+ beforeEach(async () => {
+ await DatabaseManager.init([serverUrl]);
+ });
+
+ afterEach(() => {
+ DatabaseManager.destroyServerDatabase(serverUrl);
+ });
+
+ test('makeCategoryChannelId', () => {
+ const teamId = 'my-team-id';
+ const channelId = 'my-channel-id';
+ const category = makeCategoryChannelId(teamId, channelId);
+ expect(category).toBe(`${teamId}_${channelId}`);
+ });
+
+ test('isUnreadChannel', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+
+ const channels: Channel[] = [{
+ id: 'c',
+ name: 'channel',
+ display_name: 'Channel',
+ type: 'O',
+ create_at: 1,
+ update_at: 1,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: 2,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }];
+ const myChannels: ChannelMembership[] = [
+ {
+ id: 'c',
+ user_id: 'me',
+ channel_id: 'c',
+ last_post_at: 1617311494451,
+ last_viewed_at: 1617311494451,
+ last_update_at: 1617311494451,
+ mention_count: 3,
+ msg_count: 10,
+ roles: 'guest',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ ];
+
+ const models = await db.operator.handleMyChannel({channels, myChannels, isCRTEnabled: false, prepareRecordsOnly: false});
+ expect(models).toHaveLength(1);
+ const myChannel = models[0];
+ let isUnread = isUnreadChannel(myChannel);
+ expect(isUnread).toBe(true);
+ await db.database.write(async () => {
+ await myChannel.update((record) => {
+ record.mentionsCount = 0;
+ });
+ });
+
+ isUnread = isUnreadChannel(myChannel);
+ expect(isUnread).toBe(true);
+
+ const notifyProps: Partial = {mark_unread: 'mention'};
+ isUnread = isUnreadChannel(myChannel, notifyProps);
+ expect(isUnread).toBe(false);
+
+ isUnread = isUnreadChannel(myChannel, notifyProps, myChannel.id);
+ expect(isUnread).toBe(true);
+ });
+
+ test('filterArchivedChannels', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+ const channels: Channel[] = [{
+ id: 'channel_1',
+ name: 'channel_1',
+ display_name: 'Channel 1',
+ type: 'O',
+ create_at: 1,
+ update_at: 1,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: 2,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }, {
+ id: 'channel_2',
+ name: 'channel_2',
+ display_name: 'Channel 2',
+ type: 'O',
+ create_at: 1,
+ update_at: 1,
+ delete_at: 123,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: 2,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }];
+ const myChannels: ChannelMembership[] = [
+ {
+ id: 'channel_1',
+ user_id: 'me',
+ channel_id: 'channel_1',
+ last_post_at: 1617311494451,
+ last_viewed_at: 1617311494451,
+ last_update_at: 1617311494451,
+ mention_count: 3,
+ msg_count: 10,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ {
+ id: 'channel_2',
+ user_id: 'me',
+ channel_id: 'channel_2',
+ last_post_at: 1617311494451,
+ last_viewed_at: 1617311494451,
+ last_update_at: 1617311494451,
+ mention_count: 3,
+ msg_count: 10,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ ];
+
+ const channelModels = await db.operator.handleChannel({channels, prepareRecordsOnly: false});
+ const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false});
+ const data: ChannelWithMyChannel[] = [{
+ channel: channelModels[0],
+ myChannel: myChannelModels[0],
+ sortOrder: 0,
+ }, {
+ channel: channelModels[1],
+ myChannel: myChannelModels[1],
+ sortOrder: 1,
+ }];
+ let archived = filterArchivedChannels(data, '');
+ expect(archived).toHaveLength(1);
+
+ archived = filterArchivedChannels(data, channels[1].id);
+ expect(archived).toHaveLength(2);
+ });
+
+ test('filterAutoclosedDMs', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+
+ let result = filterAutoclosedDMs('channels', 3, 'me', '', [], [], {});
+ expect(result).toEqual([]);
+
+ const now = Date.now();
+ const dt_chan1 = now - (3 * 24 * 60 * 60 * 1000);
+ const dt_chan2 = now - (2 * 24 * 60 * 60 * 1000);
+ const autoclosePrefs = [{
+ id: 'pref1',
+ userId: 'me',
+ category: Preferences.CATEGORIES.CHANNEL_APPROXIMATE_VIEW_TIME,
+ name: 'dm1',
+ value: `${dt_chan1}`,
+ }, {
+ id: 'pref2',
+ userId: 'me',
+ category: Preferences.CATEGORIES.CHANNEL_APPROXIMATE_VIEW_TIME,
+ name: 'dm2',
+ value: `${dt_chan2}`,
+ }] as PreferenceModel[];
+
+ const channels: Channel[] = [{
+ id: 'dm1',
+ name: 'me__other1',
+ display_name: 'DM 1',
+ type: 'D',
+ create_at: 1,
+ update_at: 1,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt_chan1,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }, {
+ id: 'dm2',
+ name: 'me__other2',
+ display_name: 'DM 2',
+ type: 'D',
+ create_at: 1,
+ update_at: 1,
+ delete_at: 0,
+ team_id: '123',
+ header: '',
+ purpose: '',
+ last_post_at: dt_chan2,
+ creator_id: 'me',
+ total_msg_count: 20,
+ extra_update_at: 0,
+ shared: false,
+ scheme_id: null,
+ group_constrained: false,
+ }];
+ const myChannels: ChannelMembership[] = [
+ {
+ id: 'dm1',
+ user_id: 'me',
+ channel_id: 'dm1',
+ last_post_at: dt_chan1,
+ last_viewed_at: dt_chan1,
+ last_update_at: dt_chan1,
+ mention_count: 0,
+ msg_count: 20,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ {
+ id: 'dm2',
+ user_id: 'me',
+ channel_id: 'dm2',
+ last_post_at: dt_chan2,
+ last_viewed_at: dt_chan2,
+ last_update_at: dt_chan2,
+ mention_count: 3,
+ msg_count: 10,
+ roles: 'user',
+ notify_props: {
+ desktop: 'default',
+ email: 'default',
+ mark_unread: 'mention',
+ push: 'mention',
+ ignore_channel_mentions: 'default',
+ },
+ },
+ ];
+
+ const channelModels = await db.operator.handleChannel({channels, prepareRecordsOnly: false});
+ const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false});
+ const data: ChannelWithMyChannel[] = [{
+ channel: channelModels[0],
+ myChannel: myChannelModels[0],
+ sortOrder: 0,
+ }, {
+ channel: channelModels[1],
+ myChannel: myChannelModels[1],
+ sortOrder: 0,
+ }];
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', data, autoclosePrefs, {});
+ expect(result.length).toEqual(2);
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', 'dm1', data, autoclosePrefs, {});
+ expect(result.length).toEqual(2);
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', 'dm2', data, autoclosePrefs, {});
+ expect(result.length).toEqual(2);
+
+ const lastViewedData: ChannelWithMyChannel[] = [{
+ ...data[0],
+ myChannel: {
+ ...data[0].myChannel,
+ lastViewedAt: 0,
+ } as MyChannelModel,
+ }, data[1]];
+
+ const autoclosePrefsNotViewed = [autoclosePrefs[1]];
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', lastViewedData, autoclosePrefsNotViewed, {});
+ expect(result.length).toEqual(1);
+
+ const deactivated = new Map();
+ deactivated.set('other1', {
+ id: 'other1',
+ deleteAt: dt_chan1 + 1,
+ } as UserModel);
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', data, autoclosePrefs, {}, deactivated);
+ expect(result.length).toEqual(1);
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', data, autoclosePrefs, {}, undefined, 'dm2');
+ expect(result[0].channel.id).toEqual('dm2');
+
+ const mockNotUnread: ChannelWithMyChannel[] = [data[0], {
+ ...data[1],
+ myChannel: {
+ id: data[1].myChannel.id,
+ lastViewedAt: 0,
+ isUnread: false,
+ } as MyChannelModel,
+ }];
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', mockNotUnread, autoclosePrefs, {}, undefined, 'dm1');
+ expect(result[0].channel.id).toEqual('dm1');
+
+ const mockRecent1: ChannelWithMyChannel[] = [{
+ ...data[0],
+ myChannel: {
+ id: data[0].myChannel.id,
+ lastViewedAt: 2,
+ isUnread: false,
+ } as MyChannelModel,
+ }, {
+ ...data[1],
+ myChannel: {
+ id: data[1].myChannel.id,
+ lastViewedAt: 1,
+ isUnread: false,
+ } as MyChannelModel,
+ }];
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', mockRecent1, [], {});
+ expect(result[0].channel.id).toEqual('dm1');
+
+ const mockRecent2: ChannelWithMyChannel[] = [{
+ ...data[0],
+ myChannel: {
+ id: data[0].myChannel.id,
+ lastViewedAt: 1,
+ isUnread: false,
+ } as MyChannelModel,
+ }, {
+ ...data[1],
+ myChannel: {
+ id: data[1].myChannel.id,
+ lastViewedAt: 2,
+ isUnread: false,
+ } as MyChannelModel,
+ }];
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', mockRecent2, [], {});
+ expect(result[0].channel.id).toEqual('dm2');
+
+ const mockSameLastViewed: ChannelWithMyChannel[] = [{
+ ...data[0],
+ myChannel: {
+ id: data[0].myChannel.id,
+ lastViewedAt: 1,
+ isUnread: false,
+ } as MyChannelModel,
+ }, {
+ ...data[1],
+ myChannel: {
+ id: data[1].myChannel.id,
+ lastViewedAt: 1,
+ isUnread: false,
+ } as MyChannelModel,
+ }];
+
+ result = filterAutoclosedDMs('direct_messages', 3, 'me', '', mockSameLastViewed, [], {});
+ expect(result[0].channel.id).toEqual('dm1');
+ });
+
+ test('filterManuallyClosedDms', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+
+ const closedDMsPrefs = [{
+ id: 'pref1',
+ userId: 'me',
+ category: Preferences.CATEGORIES.DIRECT_CHANNEL_SHOW,
+ name: 'dm1',
+ value: 'true',
+ }, {
+ id: 'pref2',
+ userId: 'me',
+ category: Preferences.CATEGORIES.DIRECT_CHANNEL_SHOW,
+ name: 'dm2',
+ value: 'true',
+ }, {
+ id: 'pref3',
+ userId: 'me',
+ category: Preferences.CATEGORIES.GROUP_CHANNEL_SHOW,
+ name: 'gm1',
+ value: 'false',
+ }] as PreferenceModel[];
+
+ const {channels, myChannels} = buildChannelsData();
+ const data = await createChannelsDbRecords(db, channels, myChannels);
+
+ const notifyProps = myChannels.reduce>>((obj, m) => {
+ obj[m.channel_id] = m.notify_props;
+ return obj;
+ }, {});
+
+ const result = filterManuallyClosedDms(data, notifyProps, closedDMsPrefs, 'me');
+ expect(result.length).toEqual(3);
+ });
+
+ test('sortChannels', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+
+ const {channels, myChannels} = buildChannelsData();
+ const data = await createChannelsDbRecords(db, channels, myChannels);
+
+ const notifyProps = myChannels.reduce>>((obj, m) => {
+ obj[m.channel_id] = m.notify_props;
+ return obj;
+ }, {});
+
+ let result = sortChannels('recent', data, notifyProps, 'en');
+ const recentIds = result.map((r) => r.id);
+ expect(recentIds).toEqual(['open-channel', 'dm1', 'gm1', 'dm2']);
+
+ result = sortChannels('manual', data, notifyProps, 'en');
+ const manualIds = result.map((r) => r.id);
+ expect(manualIds).toEqual(['dm1', 'dm2', 'open-channel', 'gm1']);
+
+ result = sortChannels('alpha', data, notifyProps, 'en');
+ const alphaIds = result.map((r) => r.id);
+ expect(alphaIds).toEqual(['open-channel', 'dm1', 'gm1', 'dm2']);
+ });
+
+ test('getUnreadIds', async () => {
+ const db = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+
+ const {channels, myChannels} = buildChannelsData();
+ const data = await createChannelsDbRecords(db, channels, myChannels);
+
+ const notifyProps = myChannels.reduce>>((obj, m) => {
+ obj[m.channel_id] = m.notify_props;
+ return obj;
+ }, {});
+
+ let result = getUnreadIds(data, notifyProps);
+ let ids = Array.from(result);
+ expect(ids).toEqual(['dm2', 'open-channel']);
+
+ result = getUnreadIds(data, notifyProps, 'dm1');
+ ids = Array.from(result);
+ expect(ids).toEqual(['dm1', 'dm2', 'open-channel']);
+ });
+});
diff --git a/app/utils/categories.ts b/app/utils/categories.ts
index 91f9afadc..810a902ce 100644
--- a/app/utils/categories.ts
+++ b/app/utils/categories.ts
@@ -24,7 +24,7 @@ export function makeCategoryChannelId(teamId: string, channelId: string) {
export const isUnreadChannel = (myChannel: MyChannelModel, notifyProps?: Partial, lastUnreadChannelId?: string) => {
const isMuted = notifyProps?.mark_unread === General.MENTION;
- return myChannel.mentionsCount || (!isMuted && myChannel.isUnread) || (myChannel.id === lastUnreadChannelId);
+ return Boolean(myChannel.mentionsCount) || (!isMuted && myChannel.isUnread) || (myChannel.id === lastUnreadChannelId);
};
export const filterArchivedChannels = (channelsWithMyChannel: ChannelWithMyChannel[], currentChannelId: string) => {