diff --git a/app/components/channel_icon/index.tsx b/app/components/channel_icon/index.tsx index fcd535333..3d37f8f30 100644 --- a/app/components/channel_icon/index.tsx +++ b/app/components/channel_icon/index.tsx @@ -18,6 +18,7 @@ type ChannelIconProps = { isArchived?: boolean; isInfo?: boolean; isUnread?: boolean; + isMuted?: boolean; membersCount?: number; name: string; shared: boolean; @@ -73,6 +74,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { groupInfo: { color: theme.centerChannelColor, }, + muted: { + opacity: 0.4, + }, }; }); @@ -82,6 +86,7 @@ const ChannelIcon = ({ isArchived = false, isInfo = false, isUnread = false, + isMuted = false, membersCount = 0, name, shared, @@ -99,6 +104,7 @@ const ChannelIcon = ({ let unreadGroupBox; let activeGroup; let unreadGroup; + let mutedStyle; if (isUnread) { unreadIcon = styles.iconUnread; @@ -118,6 +124,10 @@ const ChannelIcon = ({ activeGroup = styles.groupInfo; } + if (isMuted) { + mutedStyle = styles.muted; + } + let icon; if (isArchived) { icon = ( @@ -180,7 +190,7 @@ const ChannelIcon = ({ } return ( - + {icon} ); 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 2391f5257..f8bfadc08 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 @@ -65,6 +65,7 @@ Object { "width": 24, }, undefined, + undefined, ] } > @@ -104,6 +105,7 @@ Object { "paddingLeft": 12, }, false, + false, ] } > diff --git a/app/components/channel_list/categories/body/category_body.tsx b/app/components/channel_list/categories/body/category_body.tsx index c1dadddb2..605e16e09 100644 --- a/app/components/channel_list/categories/body/category_body.tsx +++ b/app/components/channel_list/categories/body/category_body.tsx @@ -14,7 +14,7 @@ type Props = { category: CategoryModel; }; -const extractKey = (item: any) => item; +const extractKey = (item: string) => item; const CategoryBody = ({currentChannelId, sortedIds, category}: Props) => { const ChannelItem = useCallback(({item}: {item: string}) => { 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 1742dfae0..9f969d335 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 @@ -58,6 +58,7 @@ exports[`components/channel_list/categories/body/channel/item should match snaps "width": 24, }, undefined, + undefined, ] } > @@ -120,6 +121,7 @@ exports[`components/channel_list/categories/body/channel/item should match snaps "paddingLeft": 12, }, false, + false, ] } > 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 4458155fc..38e297284 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 @@ -34,6 +34,7 @@ describe('components/channel_list/categories/body/channel/item', () => { isActive={false} isOwnDirectMessage={false} myChannel={myChannel} + isMuted={false} collapsed={false} />, ); 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 13a4ec1bd..f364bd3ec 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 @@ -39,6 +39,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ highlight: { color: theme.sidebarText, }, + muted: { + color: changeOpacity(theme.sidebarText, 0.4), + }, })); const textStyle = StyleSheet.create({ @@ -50,18 +53,19 @@ type Props = { channel: Pick; isActive: boolean; isOwnDirectMessage: boolean; + isMuted: boolean; myChannel: MyChannelModel; collapsed: boolean; } -const ChannelListItem = ({channel, isActive, isOwnDirectMessage, myChannel, collapsed}: Props) => { +const ChannelListItem = ({channel, isActive, isOwnDirectMessage, isMuted, myChannel, collapsed}: Props) => { const {formatMessage} = useIntl(); const theme = useTheme(); const styles = getStyleSheet(theme); const serverUrl = useServerUrl(); - // Make it brighter if it's highlighted, or has unreads - const bright = myChannel.isUnread || myChannel.mentionsCount > 0; + // Make it brighter if it's not muted, and highlighted or has unreads + const bright = !isMuted && (myChannel.isUnread || myChannel.mentionsCount > 0); const sharedValue = useSharedValue(collapsed && !bright); @@ -89,7 +93,8 @@ const ChannelListItem = ({channel, isActive, isOwnDirectMessage, myChannel, coll bright ? textStyle.bright : textStyle.regular, styles.text, bright && styles.highlight, - ], [bright, styles]); + isMuted && styles.muted, + ], [bright, styles, isMuted]); let displayName = channel.displayName; if (isOwnDirectMessage) { diff --git a/app/components/channel_list/categories/body/channel/index.ts b/app/components/channel_list/categories/body/channel/index.ts index 94b16a82b..9e0ca2717 100644 --- a/app/components/channel_list/categories/body/channel/index.ts +++ b/app/components/channel_list/categories/body/channel/index.ts @@ -15,6 +15,7 @@ import ChannelListItem from './channel_list_item'; import type {WithDatabaseArgs} from '@typings/database/database'; 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 SystemModel from '@typings/database/models/servers/system'; const {SERVER: {MY_CHANNEL, SYSTEM}} = MM_TABLES; @@ -27,6 +28,8 @@ const enhance = withObservables(['channelId'], ({channelId, database}: {channelI ); const channel = myChannel.pipe(switchMap((my) => my.channel.observe())); + const settings = channel.pipe(switchMap((c) => c.settings.observe())); + const isOwnDirectMessage = combineLatest([currentUserId, channel]).pipe( switchMap(([userId, ch]) => { if (ch?.type === General.DM_CHANNEL) { @@ -39,6 +42,9 @@ const enhance = withObservables(['channelId'], ({channelId, database}: {channelI ); return { isOwnDirectMessage, + isMuted: settings.pipe( + switchMap((s: MyChannelSettingsModel) => of$(s.notifyProps?.mark_unread === 'mention')), + ), myChannel, channel: channel.pipe( switchMap((c: ChannelModel) => of$({ diff --git a/app/database/models/server/category.ts b/app/database/models/server/category.ts index 8e651b416..5b5a15996 100644 --- a/app/database/models/server/category.ts +++ b/app/database/models/server/category.ts @@ -80,7 +80,13 @@ export default class CategoryModel extends Model implements CategoryInterface { @lazy channels = this.collections. get(CHANNEL). query( - Q.on(CATEGORY_CHANNEL, 'category_id', this.id), + Q.experimentalJoinTables([MY_CHANNEL, CATEGORY_CHANNEL]), + Q.on(CATEGORY_CHANNEL, + Q.and( + Q.on(MY_CHANNEL, Q.where('id', Q.notEq(''))), + Q.where('category_id', this.id), + ), + ), ); /** myChannels : Retrieves all myChannels that are part of this category */ diff --git a/test/test_helper.js b/test/test_helper.js index 36d8d5fa2..5bd929f0c 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -28,6 +28,7 @@ class TestHelper { this.basicChannel = null; this.basicChannelMember = null; this.basicMyChannel = null; + this.basicMyChannelSettings = null; this.basicPost = null; this.basicRoles = null; this.basicScheme = null; @@ -74,6 +75,10 @@ class TestHelper { channels: [this.basicChannel], myChannels: [this.basicMyChannel], }); + await operator.handleMyChannelSettings({ + prepareRecordsOnly: false, + settings: [this.basicMyChannelSettings], + }); const systems = await prepareCommonSystemValues(operator, { config: {}, @@ -222,6 +227,20 @@ class TestHelper { }; }; + fakeMyChannelSettings = (channelId) => { + return { + id: channelId, + channel_id: channelId, + notify_props: JSON.stringify({ + desktop: 'default', + email: 'default', + mark_unread: 'all', + push: 'default', + ignore_channel_mentions: 'default', + }), + }; + }; + fakeEmail = () => { return 'success' + this.generateId() + '@simulator.amazonses.com'; }; @@ -401,6 +420,7 @@ class TestHelper { this.basicCategoryChannel = this.fakeCategoryChannelWithId(this.basicTeam.id, this.basicCategory.id, this.basicChannel.id); this.basicChannelMember = this.fakeChannelMember(this.basicUser.id, this.basicChannel.id); this.basicMyChannel = this.fakeMyChannel(this.basicChannel.id); + this.basicMyChannelSettings = this.fakeMyChannelSettings(this.basicChannel.id); this.basicPost = {...this.fakePostWithId(this.basicChannel.id), create_at: 1507841118796}; this.basicRoles = { system_admin: {