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 <build@mattermost.com>
This commit is contained in:
parent
148f9dd716
commit
805c794b3e
10 changed files with 142 additions and 10 deletions
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
<View style={styles.separator}/>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<ChannelNotifyProps> = {
|
||||
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 (
|
||||
<OptionItem
|
||||
action={toggleFollow}
|
||||
label={intl.formatMessage({id: 'channel_info.channel_auto_follow_threads', defaultMessage: 'Follow all threads in this channel'})}
|
||||
icon='message-plus-outline'
|
||||
type='toggle'
|
||||
selected={autoFollow}
|
||||
testID={`channel_info.options.channel_auto_follow_threads.option.toggled.${autoFollow}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoFollowThreads;
|
||||
|
|
@ -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));
|
||||
|
|
@ -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<ChannelNotifyProps> = {
|
||||
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 (
|
||||
<OptionItem
|
||||
action={toggleIgnore}
|
||||
label={formatMessage({id: 'channel_info.ignore_mentions', defaultMessage: 'Ignore @channel, @here, @all'})}
|
||||
label={intl.formatMessage({id: 'channel_info.ignore_mentions', defaultMessage: 'Ignore @channel, @here, @all'})}
|
||||
icon='at'
|
||||
type='toggle'
|
||||
selected={ignored}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {of as of$} from 'rxjs';
|
|||
import {combineLatestWith, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Channel} from '@constants';
|
||||
import {observeChannelSettings} from '@queries/servers/channel';
|
||||
import {observeChannel, observeChannelSettings} from '@queries/servers/channel';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
|
||||
import IgnoreMentions from './ignore_mentions';
|
||||
|
|
@ -34,6 +34,7 @@ const isChannelMentionsIgnored = (channelNotifyProps?: Partial<ChannelNotifyProp
|
|||
};
|
||||
|
||||
const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => {
|
||||
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))),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
<IgnoreMentions channelId={channelId}/>
|
||||
}
|
||||
{type !== General.DM_CHANNEL && (
|
||||
<>
|
||||
{isCRTEnabled && (
|
||||
<AutoFollowThreads channelId={channelId}/>
|
||||
)}
|
||||
<IgnoreMentions channelId={channelId}/>
|
||||
</>
|
||||
)}
|
||||
<NotificationPreference channelId={channelId}/>
|
||||
<PinnedMessages channelId={channelId}/>
|
||||
<ChannelFiles channelId={channelId}/>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
1
types/api/channels.d.ts
vendored
1
types/api/channels.d.ts
vendored
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue