[IMPROVE] Remove show message in main thread preference (#4435)
* [IMPROVE] Remove show message in main thread preference * default settings * created the get * fix compare server version * fix E2E tests * settings to logged user * remove constant and get alsosendtochannel from user * fix send to channel first message * fix when the alsoSendThreadToChannel is checked * added list picker user preference * tweaks in messagebox tmid and detox * added pt-br and deleted expectToBeVisible id * reactive alsoSendThreadTOChannel * fix the behavior when press or long press threads messages * remove reply in thread from threads * clean helpers detox * tweak onMessageLongPress and onSubmit * Remove unnecessary calculations inside ListPicker * Fix long press logic * Fix onReplyInit logic * fix data_setup at detox for servers greater than 5.0 Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
ba15bc9fe6
commit
ccbc84f9a8
|
@ -330,7 +330,7 @@ const MessageActions = React.memo(
|
|||
let options: TActionSheetOptionsItem[] = [];
|
||||
|
||||
// Reply
|
||||
if (!isReadOnly) {
|
||||
if (!isReadOnly && !tmid) {
|
||||
options = [
|
||||
{
|
||||
title: I18n.t('Reply_in_Thread'),
|
||||
|
|
|
@ -55,7 +55,7 @@ import {
|
|||
} from '../../definitions';
|
||||
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||
import { getPermalinkMessage, search, sendFileMessage } from '../../lib/methods';
|
||||
import { hasPermission, debounce, isAndroid, isIOS, isTablet } from '../../lib/methods/helpers';
|
||||
import { hasPermission, debounce, isAndroid, isIOS, isTablet, compareServerVersion } from '../../lib/methods/helpers';
|
||||
import { Services } from '../../lib/services';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { ChatsStackParamList } from '../../stacks/types';
|
||||
|
@ -111,8 +111,8 @@ export interface IMessageBoxProps extends IBaseScreen<ChatsStackParamList & Mast
|
|||
isActionsEnabled: boolean;
|
||||
usedCannedResponse: string;
|
||||
uploadFilePermission: string[];
|
||||
serverVersion: string;
|
||||
goToCannedResponses: () => void | null;
|
||||
serverVersion: string;
|
||||
}
|
||||
|
||||
interface IMessageBoxState {
|
||||
|
@ -181,7 +181,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
commandPreview: [],
|
||||
showCommandPreview: false,
|
||||
command: {},
|
||||
tshow: false,
|
||||
tshow: this.sendThreadToChannel,
|
||||
mentionLoading: false,
|
||||
permissionToUpload: true
|
||||
};
|
||||
|
@ -211,6 +211,23 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
};
|
||||
}
|
||||
|
||||
get sendThreadToChannel() {
|
||||
const { user, serverVersion, tmid } = this.props;
|
||||
if (tmid && compareServerVersion(serverVersion, 'lowerThan', '5.0.0')) {
|
||||
return false;
|
||||
}
|
||||
if (tmid && user.alsoSendThreadToChannel === 'default') {
|
||||
return false;
|
||||
}
|
||||
if (user.alsoSendThreadToChannel === 'always') {
|
||||
return true;
|
||||
}
|
||||
if (user.alsoSendThreadToChannel === 'never') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const db = database.active;
|
||||
const { rid, tmid, navigation, sharing, usedCannedResponse, isMasterDetail } = this.props;
|
||||
|
@ -381,7 +398,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps: IMessageBoxProps) {
|
||||
const { uploadFilePermission, goToCannedResponses } = this.props;
|
||||
const { uploadFilePermission, goToCannedResponses, replyWithMention, threadsEnabled } = this.props;
|
||||
if (prevProps.replyWithMention !== replyWithMention) {
|
||||
if (threadsEnabled && replyWithMention) {
|
||||
this.setState({ tshow: this.sendThreadToChannel });
|
||||
}
|
||||
}
|
||||
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission) || prevProps.goToCannedResponses !== goToCannedResponses) {
|
||||
this.setOptions();
|
||||
}
|
||||
|
@ -687,9 +709,13 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
};
|
||||
|
||||
clearInput = () => {
|
||||
const { tshow } = this.state;
|
||||
const { user, serverVersion } = this.props;
|
||||
this.setInput('');
|
||||
this.setShowSend(false);
|
||||
this.setState({ tshow: false });
|
||||
if (compareServerVersion(serverVersion, 'lowerThan', '5.0.0') || (tshow && user.alsoSendThreadToChannel === 'default')) {
|
||||
this.setState({ tshow: false });
|
||||
}
|
||||
};
|
||||
|
||||
canUploadFile = (file: any) => {
|
||||
|
@ -974,7 +1000,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
// Normal message
|
||||
} else {
|
||||
// @ts-ignore
|
||||
onSubmit(message, undefined, tshow);
|
||||
onSubmit(message, undefined, tmid ? tshow : false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1044,7 +1070,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
onPress={this.onPressSendToChannel}
|
||||
testID='messagebox-send-to-channel'
|
||||
>
|
||||
<CustomIcon name={tshow ? 'checkbox-checked' : 'checkbox-unchecked'} size={24} color={themes[theme].auxiliaryText} />
|
||||
<CustomIcon
|
||||
testID={tshow ? 'send-to-channel-checked' : 'send-to-channel-unchecked'}
|
||||
name={tshow ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={24}
|
||||
color={themes[theme].auxiliaryText}
|
||||
/>
|
||||
<Text style={[styles.sendToChannelText, { color: themes[theme].auxiliaryText }]}>
|
||||
{I18n.t('Messagebox_Send_to_channel')}
|
||||
</Text>
|
||||
|
@ -1213,7 +1244,8 @@ const mapStateToProps = (state: IApplicationState) => ({
|
|||
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
||||
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
||||
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled,
|
||||
uploadFilePermission: state.permissions['mobile-upload-file']
|
||||
uploadFilePermission: state.permissions['mobile-upload-file'],
|
||||
serverVersion: state.server.version
|
||||
});
|
||||
|
||||
const dispatchToProps = {
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface ILoggedUser {
|
|||
showMessageInMainThread?: boolean;
|
||||
isFromWebView?: boolean;
|
||||
enableMessageParserEarlyAdoption: boolean;
|
||||
alsoSendThreadToChannel: 'default' | 'always' | 'never';
|
||||
}
|
||||
|
||||
export interface ILoggedUserResultFromServer
|
||||
|
|
|
@ -842,5 +842,7 @@
|
|||
"error-init-video-conf": "Error starting video call",
|
||||
"totp-invalid": "Code or password invalid",
|
||||
"Close_Chat": "Close Chat",
|
||||
"Select_tags": "Select tags"
|
||||
"Select_tags": "Select tags",
|
||||
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
|
||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior"
|
||||
}
|
|
@ -795,5 +795,7 @@
|
|||
"Show_badge_for_mentions_Info": "Mostrar contador somente para menções diretas",
|
||||
"totp-invalid": "Código ou senha inválida",
|
||||
"Close_Chat": "Fechar Conversa",
|
||||
"Select_tags": "Selecionar tag(s)"
|
||||
"Select_tags": "Selecionar tag(s)",
|
||||
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
|
||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal"
|
||||
}
|
|
@ -295,6 +295,9 @@ export default function subscribeRooms() {
|
|||
if ((['settings.preferences.showMessageInMainThread'] as any) in diff) {
|
||||
store.dispatch(setUser({ showMessageInMainThread: diff['settings.preferences.showMessageInMainThread'] }));
|
||||
}
|
||||
if ((['settings.preferences.alsoSendThreadToChannel'] as any) in diff) {
|
||||
store.dispatch(setUser({ alsoSendThreadToChannel: diff['settings.preferences.alsoSendThreadToChannel'] }));
|
||||
}
|
||||
}
|
||||
if (/subscriptions/.test(ev)) {
|
||||
if (type === 'removed') {
|
||||
|
|
|
@ -268,8 +268,10 @@ async function login(credentials: ICredentials, isFromWebView = false): Promise<
|
|||
const result = sdk.current.currentLogin?.result;
|
||||
|
||||
let enableMessageParserEarlyAdoption = true;
|
||||
let showMessageInMainThread = false;
|
||||
if (compareServerVersion(serverVersion, 'lowerThan', '5.0.0')) {
|
||||
enableMessageParserEarlyAdoption = result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true;
|
||||
showMessageInMainThread = result.me.settings?.preferences?.showMessageInMainThread ?? true;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
@ -287,8 +289,9 @@ async function login(credentials: ICredentials, isFromWebView = false): Promise<
|
|||
roles: result.me.roles,
|
||||
avatarETag: result.me.avatarETag,
|
||||
isFromWebView,
|
||||
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true,
|
||||
enableMessageParserEarlyAdoption
|
||||
showMessageInMainThread,
|
||||
enableMessageParserEarlyAdoption,
|
||||
alsoSendThreadToChannel: result.me.settings?.preferences?.alsoSendThreadToChannel
|
||||
};
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -813,6 +813,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
};
|
||||
|
||||
onReplyInit = (message: TAnyMessageModel, mention: boolean) => {
|
||||
// If there's a thread already, we redirect to it
|
||||
if (mention && !!message.tlm) {
|
||||
return this.onThreadPress(message);
|
||||
}
|
||||
this.setState({
|
||||
selectedMessage: message,
|
||||
replying: true,
|
||||
|
@ -833,6 +837,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
};
|
||||
|
||||
onMessageLongPress = (message: TAnyMessageModel) => {
|
||||
// if it's a thread message on main room, we disable the long press
|
||||
if (message.tmid && !this.tmid) {
|
||||
return;
|
||||
}
|
||||
this.messagebox?.current?.closeEmojiAndAction(this.messageActions?.showMessageActions, message);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
|
||||
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
|
||||
import { CustomIcon } from '../../containers/CustomIcon';
|
||||
import * as List from '../../containers/List';
|
||||
import I18n from '../../i18n';
|
||||
import { useTheme } from '../../theme';
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: { ...sharedStyles.textRegular, fontSize: 16 }
|
||||
});
|
||||
|
||||
const OPTIONS = {
|
||||
alsoSendThreadToChannel: [
|
||||
{
|
||||
label: 'Default',
|
||||
value: 'default'
|
||||
},
|
||||
{
|
||||
label: 'Always',
|
||||
value: 'always'
|
||||
},
|
||||
{
|
||||
label: 'Never',
|
||||
value: 'never'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
type TOptions = keyof typeof OPTIONS;
|
||||
|
||||
interface IBaseParams {
|
||||
preference: TOptions;
|
||||
value: string;
|
||||
onChangeValue: (param: { [key: string]: string }, onError: () => void) => void;
|
||||
}
|
||||
|
||||
const ListPicker = ({
|
||||
preference,
|
||||
value,
|
||||
title,
|
||||
testID,
|
||||
onChangeValue
|
||||
}: {
|
||||
title: string;
|
||||
testID: string;
|
||||
} & IBaseParams) => {
|
||||
const { showActionSheet, hideActionSheet } = useActionSheet();
|
||||
const { colors } = useTheme();
|
||||
const [option, setOption] = useState(
|
||||
value ? OPTIONS[preference].find(option => option.value === value) : OPTIONS[preference][0]
|
||||
);
|
||||
|
||||
const getOptions = (): TActionSheetOptionsItem[] =>
|
||||
OPTIONS[preference].map(i => ({
|
||||
title: I18n.t(i.label, { defaultValue: i.label }),
|
||||
onPress: () => {
|
||||
hideActionSheet();
|
||||
onChangeValue({ [preference]: i.value.toString() }, () => setOption(option));
|
||||
setOption(i);
|
||||
},
|
||||
right: option?.value === i.value ? () => <CustomIcon name={'check'} size={20} color={colors.tintActive} /> : undefined
|
||||
}));
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
title={title}
|
||||
testID={testID}
|
||||
onPress={() => showActionSheet({ options: getOptions() })}
|
||||
right={() => (
|
||||
<Text style={[styles.title, { color: colors.actionTintColor }]}>
|
||||
{option?.label ? I18n.t(option?.label, { defaultValue: option?.label }) : option?.label}
|
||||
</Text>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListPicker;
|
|
@ -15,13 +15,14 @@ import { getUserSelector } from '../../selectors/login';
|
|||
import { ProfileStackParamList } from '../../stacks/types';
|
||||
import { Services } from '../../lib/services';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import ListPicker from './ListPicker';
|
||||
|
||||
interface IUserPreferencesViewProps {
|
||||
navigation: StackNavigationProp<ProfileStackParamList, 'UserPreferencesView'>;
|
||||
}
|
||||
|
||||
const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Element => {
|
||||
const { enableMessageParserEarlyAdoption, id } = useAppSelector(state => getUserSelector(state));
|
||||
const { enableMessageParserEarlyAdoption, id, alsoSendThreadToChannel } = useAppSelector(state => getUserSelector(state));
|
||||
const serverVersion = useAppSelector(state => state.server.version);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
@ -45,6 +46,16 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele
|
|||
}
|
||||
};
|
||||
|
||||
const setAlsoSendThreadToChannel = async (param: { [key: string]: string }, onError: () => void) => {
|
||||
try {
|
||||
await Services.saveUserPreferences(param);
|
||||
dispatch(setUser(param));
|
||||
} catch (e) {
|
||||
log(e);
|
||||
onError();
|
||||
}
|
||||
};
|
||||
|
||||
const renderMessageParserSwitch = (value: boolean) => (
|
||||
<Switch value={value} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleMessageParser} />
|
||||
);
|
||||
|
@ -74,6 +85,20 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele
|
|||
<List.Separator />
|
||||
</List.Section>
|
||||
) : null}
|
||||
{compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0') ? (
|
||||
<List.Section title='Also_send_thread_message_to_channel_behavior'>
|
||||
<List.Separator />
|
||||
<ListPicker
|
||||
onChangeValue={setAlsoSendThreadToChannel}
|
||||
preference='alsoSendThreadToChannel'
|
||||
value={alsoSendThreadToChannel}
|
||||
title='Messagebox_Send_to_channel'
|
||||
testID='preferences-view-enable-message-parser'
|
||||
/>
|
||||
<List.Separator />
|
||||
<List.Info info='Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description' />
|
||||
</List.Section>
|
||||
) : null}
|
||||
</List.Container>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const axios = require('axios').default;
|
||||
|
||||
const data = require('../data');
|
||||
const random = require('./random');
|
||||
|
||||
const TEAM_TYPE = {
|
||||
PUBLIC: 0,
|
||||
|
@ -106,6 +107,8 @@ const changeChannelJoinCode = async (roomId, joinCode) => {
|
|||
try {
|
||||
await rocketchat.post('method.call/saveRoomSettings', {
|
||||
message: JSON.stringify({
|
||||
msg: 'method',
|
||||
id: random(10),
|
||||
method: 'saveRoomSettings',
|
||||
params: [roomId, { joinCode }]
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue