From 551a01fd4b066f8fc393398095c69119684e5f39 Mon Sep 17 00:00:00 2001 From: Tanmay Vardhaman Thole <72058456+tanmaythole@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:30:19 +0530 Subject: [PATCH] MM 45015 - auto follow threads (#7463) * auto follow select option added * unused code removed * auto follow threads condition fixed on CRT enabled * error handles --------- Co-authored-by: Mattermost Build --- app/constants/channel.ts | 4 ++ app/screens/channel_info/channel_info.tsx | 3 + app/screens/channel_info/index.ts | 2 + .../auto_follow_threads.tsx | 61 +++++++++++++++++++ .../options/auto_follow_threads/index.ts | 35 +++++++++++ .../ignore_mentions/ignore_mentions.tsx | 27 ++++++-- .../options/ignore_mentions/index.ts | 4 +- app/screens/channel_info/options/index.tsx | 14 ++++- assets/base/i18n/en.json | 1 + types/api/channels.d.ts | 1 + 10 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 app/screens/channel_info/options/auto_follow_threads/auto_follow_threads.tsx create mode 100644 app/screens/channel_info/options/auto_follow_threads/index.ts diff --git a/app/constants/channel.ts b/app/constants/channel.ts index 2c94cb3e92c..8169187a7ae 100644 --- a/app/constants/channel.ts +++ b/app/constants/channel.ts @@ -6,6 +6,8 @@ export const MAX_CHANNEL_NAME_LENGTH = 64; export const IGNORE_CHANNEL_MENTIONS_ON = 'on'; export const IGNORE_CHANNEL_MENTIONS_OFF = 'off'; export const IGNORE_CHANNEL_MENTIONS_DEFAULT = 'default'; +export const CHANNEL_AUTO_FOLLOW_THREADS_TRUE = 'on'; +export const CHANNEL_AUTO_FOLLOW_THREADS_FALSE = 'off'; export default { IGNORE_CHANNEL_MENTIONS_ON, @@ -13,4 +15,6 @@ export default { IGNORE_CHANNEL_MENTIONS_DEFAULT, MAX_CHANNEL_NAME_LENGTH, MIN_CHANNEL_NAME_LENGTH, + CHANNEL_AUTO_FOLLOW_THREADS_TRUE, + CHANNEL_AUTO_FOLLOW_THREADS_FALSE, }; diff --git a/app/screens/channel_info/channel_info.tsx b/app/screens/channel_info/channel_info.tsx index 63292e136e4..8298427b933 100644 --- a/app/screens/channel_info/channel_info.tsx +++ b/app/screens/channel_info/channel_info.tsx @@ -30,6 +30,7 @@ type Props = { canEnableDisableCalls: boolean; isCallsEnabledInChannel: boolean; canManageMembers: boolean; + isCRTEnabled: boolean; canManageSettings: boolean; } @@ -51,6 +52,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ })); const ChannelInfo = ({ + isCRTEnabled, channelId, closeButtonId, componentId, @@ -105,6 +107,7 @@ const ChannelInfo = ({ type={type} callsEnabled={callsAvailable} canManageMembers={canManageMembers} + isCRTEnabled={isCRTEnabled} canManageSettings={canManageSettings} /> diff --git a/app/screens/channel_info/index.ts b/app/screens/channel_info/index.ts index 3cdbd799f32..acff1488a18 100644 --- a/app/screens/channel_info/index.ts +++ b/app/screens/channel_info/index.ts @@ -17,6 +17,7 @@ import { observeCurrentTeamId, observeCurrentUserId, } from '@queries/servers/system'; +import {observeIsCRTEnabled} from '@queries/servers/thread'; import {observeCurrentUser, observeUserIsChannelAdmin, observeUserIsTeamAdmin} from '@queries/servers/user'; import {isTypeDMorGM} from '@utils/channel'; import {isMinimumServerVersion} from '@utils/helpers'; @@ -117,6 +118,7 @@ const enhanced = withObservables([], ({serverUrl, database}: Props) => { canEnableDisableCalls, isCallsEnabledInChannel, canManageMembers, + isCRTEnabled: observeIsCRTEnabled(database), canManageSettings, }; }); diff --git a/app/screens/channel_info/options/auto_follow_threads/auto_follow_threads.tsx b/app/screens/channel_info/options/auto_follow_threads/auto_follow_threads.tsx new file mode 100644 index 00000000000..5421b1ff748 --- /dev/null +++ b/app/screens/channel_info/options/auto_follow_threads/auto_follow_threads.tsx @@ -0,0 +1,61 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {updateChannelNotifyProps} from '@actions/remote/channel'; +import OptionItem from '@components/option_item'; +import { + CHANNEL_AUTO_FOLLOW_THREADS_FALSE, + CHANNEL_AUTO_FOLLOW_THREADS_TRUE, +} from '@constants/channel'; +import {useServerUrl} from '@context/server'; +import {t} from '@i18n'; +import {alertErrorWithFallback} from '@utils/draft'; +import {preventDoubleTap} from '@utils/tap'; + +type Props = { + channelId: string; + followedStatus: boolean; + displayName: string; +}; + +const AutoFollowThreads = ({channelId, displayName, followedStatus}: Props) => { + const [autoFollow, setAutoFollow] = useState(followedStatus); + const serverUrl = useServerUrl(); + const intl = useIntl(); + + const toggleFollow = preventDoubleTap(async () => { + const props: Partial = { + channel_auto_follow_threads: followedStatus ? CHANNEL_AUTO_FOLLOW_THREADS_FALSE : CHANNEL_AUTO_FOLLOW_THREADS_TRUE, + }; + setAutoFollow((v) => !v); + const result = await updateChannelNotifyProps(serverUrl, channelId, props); + if (result?.error) { + alertErrorWithFallback( + intl, + result.error, + { + id: t('channel_info.channel_auto_follow_threads_failed'), + defaultMessage: 'An error occurred trying to auto follow all threads in channel {displayName}', + }, + {displayName}, + ); + setAutoFollow((v) => !v); + } + }); + + return ( + + ); +}; + +export default AutoFollowThreads; diff --git a/app/screens/channel_info/options/auto_follow_threads/index.ts b/app/screens/channel_info/options/auto_follow_threads/index.ts new file mode 100644 index 00000000000..f2a7a993a2b --- /dev/null +++ b/app/screens/channel_info/options/auto_follow_threads/index.ts @@ -0,0 +1,35 @@ +// 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 {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {Channel} from '@constants'; +import {observeChannel, observeChannelSettings} from '@queries/servers/channel'; + +import AutoFollowThreads from './auto_follow_threads'; + +import type {WithDatabaseArgs} from '@typings/database/database'; + +type Props = WithDatabaseArgs & { + channelId: string; +} + +const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => { + const channel = observeChannel(database, channelId); + const settings = observeChannelSettings(database, channelId); + const followedStatus = settings.pipe( + switchMap((s) => { + return of$(s?.notifyProps?.channel_auto_follow_threads === Channel.CHANNEL_AUTO_FOLLOW_THREADS_TRUE); + }), + ); + + return { + followedStatus, + displayName: channel.pipe(switchMap((c) => of$(c?.displayName))), + }; +}); + +export default withDatabase(enhanced(AutoFollowThreads)); diff --git a/app/screens/channel_info/options/ignore_mentions/ignore_mentions.tsx b/app/screens/channel_info/options/ignore_mentions/ignore_mentions.tsx index 2fef6479b93..a9f0aaa97a8 100644 --- a/app/screens/channel_info/options/ignore_mentions/ignore_mentions.tsx +++ b/app/screens/channel_info/options/ignore_mentions/ignore_mentions.tsx @@ -5,6 +5,8 @@ import React, {useState} from 'react'; import {useIntl} from 'react-intl'; import {updateChannelNotifyProps} from '@actions/remote/channel'; +import {t} from '@app/i18n'; +import {alertErrorWithFallback} from '@app/utils/draft'; import OptionItem from '@components/option_item'; import {useServerUrl} from '@context/server'; import {preventDoubleTap} from '@utils/tap'; @@ -12,25 +14,38 @@ import {preventDoubleTap} from '@utils/tap'; type Props = { channelId: string; ignoring: boolean; + displayName: string; } -const IgnoreMentions = ({channelId, ignoring}: Props) => { +const IgnoreMentions = ({channelId, ignoring, displayName}: Props) => { const [ignored, setIgnored] = useState(ignoring); const serverUrl = useServerUrl(); - const {formatMessage} = useIntl(); + const intl = useIntl(); - const toggleIgnore = preventDoubleTap(() => { + const toggleIgnore = preventDoubleTap(async () => { const props: Partial = { ignore_channel_mentions: ignoring ? 'off' : 'on', }; - setIgnored(!ignored); - updateChannelNotifyProps(serverUrl, channelId, props); + setIgnored((v) => !v); + const result = await updateChannelNotifyProps(serverUrl, channelId, props); + if (result?.error) { + alertErrorWithFallback( + intl, + result.error, + { + id: t('channel_info.channel_auto_follow_threads_failed'), + defaultMessage: 'An error occurred trying to auto follow all threads in channel {displayName}', + }, + {displayName}, + ); + setIgnored((v) => !v); + } }); return ( { + const channel = observeChannel(database, channelId); const currentUser = observeCurrentUser(database); const settings = observeChannelSettings(database, channelId); const ignoring = currentUser.pipe( @@ -43,6 +44,7 @@ const enhanced = withObservables(['channelId'], ({channelId, database}: Props) = return { ignoring, + displayName: channel.pipe(switchMap((c) => of$(c?.displayName))), }; }); diff --git a/app/screens/channel_info/options/index.tsx b/app/screens/channel_info/options/index.tsx index 816fc286725..ed18375748f 100644 --- a/app/screens/channel_info/options/index.tsx +++ b/app/screens/channel_info/options/index.tsx @@ -8,6 +8,7 @@ import {General} from '@constants'; import {isTypeDMorGM} from '@utils/channel'; import AddMembers from './add_members'; +import AutoFollowThreads from './auto_follow_threads'; import ChannelFiles from './channel_files'; import EditChannel from './edit_channel'; import IgnoreMentions from './ignore_mentions'; @@ -20,6 +21,7 @@ type Props = { type?: ChannelType; callsEnabled: boolean; canManageMembers: boolean; + isCRTEnabled: boolean; canManageSettings: boolean; } @@ -28,15 +30,21 @@ const Options = ({ type, callsEnabled, canManageMembers, + isCRTEnabled, canManageSettings, }: Props) => { const isDMorGM = isTypeDMorGM(type); return ( <> - {type !== General.DM_CHANNEL && - - } + {type !== General.DM_CHANNEL && ( + <> + {isCRTEnabled && ( + + )} + + + )} diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 10de3c30d2d..4a0e3c01991 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -112,6 +112,7 @@ "channel_info.archive_description.cannot_view_archived": "This will archive the channel from the team and remove it from the user interface. Archived channels can be unarchived if needed again.\n\nAre you sure you wish to archive the {term} {name}?", "channel_info.archive_failed": "An error occurred trying to archive the channel {displayName}", "channel_info.archive_title": "Archive {term}", + "channel_info.channel_auto_follow_threads": "Follow all threads in this channel", "channel_info.channel_files": "Files", "channel_info.close": "Close", "channel_info.close_dm": "Close direct message", diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index ef0f2a6595f..b176b15f0a8 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -17,6 +17,7 @@ type ChannelNotifyProps = { mark_unread: 'all' | 'mention'; push: NotificationLevel; ignore_channel_mentions: 'default' | 'off' | 'on'; + channel_auto_follow_threads: 'on' | 'off'; push_threads: 'all' | 'mention'; }; type Channel = {