Chore: Migrate LivechatEditView to Typescript (#3499)
* Chore: Migrate LivechatEditView to Typescript * refactor: minor tweak * refactor: fix the interfaces for input * refactor: fix lint erros * minor tweak with new navigation types * function * iroom tweak * livechateditview tweak * TextInput tweak * refactor: update new types and interfaces for use ISubscription * refactor to default useState type * change the component name in SearchBox * changed state type Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
6933278fd5
commit
0289ba716d
|
@ -1,5 +1,13 @@
|
|||
import React from 'react';
|
||||
import { NativeSyntheticEvent, StyleSheet, Text, TextInputFocusEventData, TextInputProps, View } from 'react-native';
|
||||
import {
|
||||
NativeSyntheticEvent,
|
||||
StyleSheet,
|
||||
TextInput as RNTextInput,
|
||||
Text,
|
||||
TextInputFocusEventData,
|
||||
TextInputProps,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import TextInput from '../presentation/TextInput';
|
||||
|
@ -51,7 +59,7 @@ interface ISearchBox {
|
|||
hasCancel?: boolean;
|
||||
onCancelPress?: Function;
|
||||
theme?: string;
|
||||
inputRef?: React.Ref<unknown>;
|
||||
inputRef?: React.Ref<RNTextInput>;
|
||||
testID?: string;
|
||||
onFocus?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, StyleSheet, Text, TextInputProps, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { StyleProp, StyleSheet, Text, TextInputProps, TextInput as RNTextInput, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import sharedStyles from '../views/Styles';
|
||||
|
@ -59,7 +59,7 @@ export interface IRCTextInputProps extends TextInputProps {
|
|||
loading?: boolean;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
inputStyle?: StyleProp<TextStyle>;
|
||||
inputRef?: React.Ref<unknown>;
|
||||
inputRef?: React.Ref<RNTextInput>;
|
||||
testID?: string;
|
||||
iconLeft?: string;
|
||||
iconRight?: string;
|
||||
|
|
|
@ -22,8 +22,8 @@ interface IMultiSelect {
|
|||
context?: number;
|
||||
loading?: boolean;
|
||||
multiselect?: boolean;
|
||||
onSearch: Function;
|
||||
onClose: Function;
|
||||
onSearch?: () => void;
|
||||
onClose?: () => void;
|
||||
inputStyle: object;
|
||||
value?: any[];
|
||||
disabled?: boolean | object;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
export interface ITagsOmnichannel {
|
||||
_id: string;
|
||||
name: string;
|
||||
departments: string[];
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
export interface IVisitorEmail {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface IVisitorPhone {
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
export interface IVisitor {
|
||||
_id?: string;
|
||||
token: string;
|
||||
username: string;
|
||||
updatedAt?: Date;
|
||||
name: string;
|
||||
department?: string;
|
||||
phone?: IVisitorPhone[];
|
||||
visitorEmails?: IVisitorEmail[];
|
||||
customFields?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
livechatData: {
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
|
@ -15,7 +15,7 @@ interface IThemedTextInput extends IRCTextInputProps {
|
|||
theme: string;
|
||||
}
|
||||
|
||||
const ThemedTextInput = React.forwardRef(({ style, theme, ...props }: IThemedTextInput, ref: any) => (
|
||||
const ThemedTextInput = React.forwardRef<TextInput, IThemedTextInput>(({ style, theme, ...props }, ref) => (
|
||||
<TextInput
|
||||
ref={ref}
|
||||
style={[{ color: themes[theme].titleText }, style, styles.input]}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { StackNavigationOptions } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { ScrollView, StyleSheet, Text } from 'react-native';
|
||||
import { ScrollView, StyleSheet, Text, TextInput as RNTextInput } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { encryptionDecodeKey } from '../actions/encryption';
|
||||
|
@ -37,7 +37,7 @@ interface IE2EEnterYourPasswordViewState {
|
|||
type TE2EEnterYourPasswordViewProps = IBaseScreen<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
|
||||
|
||||
class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> {
|
||||
private passwordInput?: TextInput;
|
||||
private passwordInput?: RNTextInput;
|
||||
|
||||
static navigationOptions = ({ navigation }: Pick<TE2EEnterYourPasswordViewProps, 'navigation'>): StackNavigationOptions => ({
|
||||
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='e2e-enter-your-password-view-close' />,
|
||||
|
@ -76,7 +76,7 @@ class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordView
|
|||
style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
testID='e2e-enter-your-password-view'>
|
||||
<TextInput
|
||||
inputRef={(e: TextInput) => {
|
||||
inputRef={(e: RNTextInput) => {
|
||||
this.passwordInput = e;
|
||||
}}
|
||||
placeholder={I18n.t('Password')}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ScrollView, StyleSheet, Text } from 'react-native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import { RouteProp } from '@react-navigation/native';
|
||||
import { ScrollView, StyleSheet, Text, TextInput as RNTextInput } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
|
||||
|
@ -10,7 +11,6 @@ import TextInput from '../containers/TextInput';
|
|||
import KeyboardView from '../presentation/KeyboardView';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import I18n from '../i18n';
|
||||
|
||||
import { LISTENER } from '../containers/Toast';
|
||||
import EventEmitter from '../utils/events';
|
||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||
|
@ -18,6 +18,10 @@ import { getUserSelector } from '../selectors/login';
|
|||
import Button from '../containers/Button';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import { MultiSelect } from '../containers/UIKit/MultiSelect';
|
||||
import { IVisitor } from '../definitions/IVisitor';
|
||||
import { ITagsOmnichannel } from '../definitions/ITagsOmnichannel';
|
||||
import { IApplicationState, ISubscription } from '../definitions';
|
||||
import { ChatsStackParamList } from '../stacks/types';
|
||||
import sharedStyles from './Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -39,36 +43,87 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const Title = ({ title, theme }) =>
|
||||
title ? <Text style={[styles.title, { color: themes[theme].titleText }]}>{title}</Text> : null;
|
||||
Title.propTypes = {
|
||||
title: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
interface ITitle {
|
||||
title: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelContact, editLivechatRoomCustomfields }) => {
|
||||
const [customFields, setCustomFields] = useState({});
|
||||
const [availableUserTags, setAvailableUserTags] = useState([]);
|
||||
interface IField {
|
||||
_id: string;
|
||||
visibility: string;
|
||||
scope: string;
|
||||
}
|
||||
|
||||
interface IInputs {
|
||||
[key: string]: string | string[] | undefined;
|
||||
name: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
topic: string;
|
||||
tag: string[];
|
||||
}
|
||||
|
||||
type TParams = IVisitor & IInputs;
|
||||
|
||||
interface ILivechat extends ISubscription {
|
||||
// Param dynamic depends on server
|
||||
sms?: string;
|
||||
}
|
||||
|
||||
interface IInputsRefs {
|
||||
[index: string]: RNTextInput | null;
|
||||
name: RNTextInput | null;
|
||||
phone: RNTextInput | null;
|
||||
topic: RNTextInput | null;
|
||||
}
|
||||
|
||||
interface ICustomFields {
|
||||
visitor?: { [key: string]: string };
|
||||
livechat?: { [key: string]: string };
|
||||
}
|
||||
interface ILivechatEditViewProps {
|
||||
// TODO: Refactor when migrate models
|
||||
user: any;
|
||||
navigation: StackNavigationProp<ChatsStackParamList, 'LivechatEditView'>;
|
||||
route: RouteProp<ChatsStackParamList, 'LivechatEditView'>;
|
||||
theme: string;
|
||||
editOmnichannelContact: string[];
|
||||
editLivechatRoomCustomfields: string[];
|
||||
}
|
||||
|
||||
const Title = ({ title, theme }: ITitle) =>
|
||||
title ? <Text style={[styles.title, { color: themes[theme].titleText }]}>{title}</Text> : null;
|
||||
|
||||
const LivechatEditView = ({
|
||||
user,
|
||||
navigation,
|
||||
route,
|
||||
theme,
|
||||
editOmnichannelContact,
|
||||
editLivechatRoomCustomfields
|
||||
}: ILivechatEditViewProps) => {
|
||||
const [customFields, setCustomFields] = useState<ICustomFields>({});
|
||||
const [availableUserTags, setAvailableUserTags] = useState<string[]>([]);
|
||||
const [permissions, setPermissions] = useState([]);
|
||||
|
||||
const params = {};
|
||||
const inputs = {};
|
||||
const params = {} as TParams;
|
||||
const inputs = {} as IInputsRefs;
|
||||
|
||||
const livechat = route.params?.room ?? {};
|
||||
const livechat = (route.params?.room ?? {}) as ILivechat;
|
||||
const visitor = route.params?.roomUser ?? {};
|
||||
|
||||
const getCustomFields = async () => {
|
||||
const result = await RocketChat.getCustomFields();
|
||||
if (result.success && result.customFields?.length) {
|
||||
const visitorCustomFields = result.customFields
|
||||
.filter(field => field.visibility !== 'hidden' && field.scope === 'visitor')
|
||||
.map(field => ({ [field._id]: (visitor.livechatData && visitor.livechatData[field._id]) || '' }))
|
||||
.reduce((ret, field) => ({ ...field, ...ret }));
|
||||
.filter((field: IField) => field.visibility !== 'hidden' && field.scope === 'visitor')
|
||||
.map((field: IField) => ({ [field._id]: (visitor.livechatData && visitor.livechatData[field._id]) || '' }))
|
||||
.reduce((ret: IField, field: IField) => ({ ...field, ...ret }));
|
||||
|
||||
const livechatCustomFields = result.customFields
|
||||
.filter(field => field.visibility !== 'hidden' && field.scope === 'room')
|
||||
.map(field => ({ [field._id]: (livechat.livechatData && livechat.livechatData[field._id]) || '' }))
|
||||
.reduce((ret, field) => ({ ...field, ...ret }));
|
||||
.filter((field: IField) => field.visibility !== 'hidden' && field.scope === 'room')
|
||||
.map((field: IField) => ({ [field._id]: (livechat.livechatData && livechat.livechatData[field._id]) || '' }))
|
||||
.reduce((ret: IField, field: IField) => ({ ...field, ...ret }));
|
||||
|
||||
return setCustomFields({ visitor: visitorCustomFields, livechat: livechatCustomFields });
|
||||
}
|
||||
|
@ -83,8 +138,8 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
setTags(uniqueArray);
|
||||
}, [availableUserTags]);
|
||||
|
||||
const getTagsList = async agentDepartments => {
|
||||
const tags = await RocketChat.getTagsList();
|
||||
const getTagsList = async (agentDepartments: string[]) => {
|
||||
const tags: ITagsOmnichannel[] = await RocketChat.getTagsList();
|
||||
const isAdmin = ['admin', 'livechat-manager'].find(role => user.roles.includes(role));
|
||||
const availableTags = tags
|
||||
.filter(({ departments }) => isAdmin || departments.length === 0 || departments.some(i => agentDepartments.indexOf(i) > -1))
|
||||
|
@ -95,16 +150,18 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
const getAgentDepartments = async () => {
|
||||
const result = await RocketChat.getAgentDepartments(visitor?._id);
|
||||
if (result.success) {
|
||||
const agentDepartments = result.departments.map(dept => dept.departmentId);
|
||||
const agentDepartments = result.departments.map((dept: { departmentId: string }) => dept.departmentId);
|
||||
getTagsList(agentDepartments);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const userData = { _id: visitor?._id };
|
||||
const userData = { _id: visitor?._id } as TParams;
|
||||
|
||||
const { rid, sms } = livechat;
|
||||
const roomData = { _id: rid };
|
||||
const { rid } = livechat;
|
||||
const sms = livechat?.sms;
|
||||
|
||||
const roomData = { _id: rid } as TParams;
|
||||
|
||||
if (params.name) {
|
||||
userData.name = params.name;
|
||||
|
@ -149,7 +206,7 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
}
|
||||
};
|
||||
|
||||
const onChangeText = (key, text) => {
|
||||
const onChangeText = (key: string, text: string) => {
|
||||
params[key] = text;
|
||||
};
|
||||
|
||||
|
@ -159,6 +216,9 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: I18n.t('Edit')
|
||||
});
|
||||
getAgentDepartments();
|
||||
getCustomFields();
|
||||
getPermissions();
|
||||
|
@ -177,7 +237,7 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
defaultValue={visitor?.name}
|
||||
onChangeText={text => onChangeText('name', text)}
|
||||
onSubmitEditing={() => {
|
||||
inputs.name.focus();
|
||||
inputs.name?.focus();
|
||||
}}
|
||||
theme={theme}
|
||||
editable={!!permissions[0]}
|
||||
|
@ -190,7 +250,7 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
defaultValue={visitor?.visitorEmails && visitor?.visitorEmails[0]?.address}
|
||||
onChangeText={text => onChangeText('email', text)}
|
||||
onSubmitEditing={() => {
|
||||
inputs.phone.focus();
|
||||
inputs.phone?.focus();
|
||||
}}
|
||||
theme={theme}
|
||||
editable={!!permissions[0]}
|
||||
|
@ -206,9 +266,9 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
const keys = Object.keys(customFields?.visitor || {});
|
||||
if (keys.length > 0) {
|
||||
const key = keys[0];
|
||||
inputs[key].focus();
|
||||
inputs[key]?.focus();
|
||||
} else {
|
||||
inputs.topic.focus();
|
||||
inputs.topic?.focus();
|
||||
}
|
||||
}}
|
||||
theme={theme}
|
||||
|
@ -224,9 +284,9 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
onChangeText={text => onChangeText(key, text)}
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
return inputs[array[index + 1][0]].focus();
|
||||
return inputs[array[index + 1][0]]?.focus();
|
||||
}
|
||||
inputs.topic.focus();
|
||||
inputs.topic?.focus();
|
||||
}}
|
||||
theme={theme}
|
||||
editable={!!permissions[0]}
|
||||
|
@ -240,15 +300,14 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
}}
|
||||
defaultValue={livechat?.topic}
|
||||
onChangeText={text => onChangeText('topic', text)}
|
||||
onSubmitEditing={() => inputs.tags.focus()}
|
||||
theme={theme}
|
||||
editable={!!permissions[1]}
|
||||
/>
|
||||
|
||||
<Text style={[styles.label, { color: themes[theme].titleText }]}>{I18n.t('Tags')}</Text>
|
||||
<MultiSelect
|
||||
options={tagParam.map(tag => ({ text: { text: tag }, value: tag }))}
|
||||
onChange={({ value }) => {
|
||||
options={tagParam.map((tag: string) => ({ text: { text: tag }, value: tag }))}
|
||||
onChange={({ value }: { value: string[] }) => {
|
||||
setTagParamSelected([...value]);
|
||||
}}
|
||||
placeholder={{ text: I18n.t('Tags') }}
|
||||
|
@ -260,7 +319,7 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
inputStyle={styles.multiSelect}
|
||||
/>
|
||||
|
||||
{Object.entries(customFields?.livechat || {}).map(([key, value], index, array) => (
|
||||
{Object.entries(customFields?.livechat || {}).map(([key, value], index, array: any) => (
|
||||
<TextInput
|
||||
label={key}
|
||||
defaultValue={value}
|
||||
|
@ -270,7 +329,7 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
onChangeText={text => onChangeText(key, text)}
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
return inputs[array[index + 1]].focus();
|
||||
return inputs[array[index + 1]]?.focus();
|
||||
}
|
||||
submit();
|
||||
}}
|
||||
|
@ -285,19 +344,8 @@ const LivechatEditView = ({ user, navigation, route, theme, editOmnichannelConta
|
|||
</KeyboardView>
|
||||
);
|
||||
};
|
||||
LivechatEditView.propTypes = {
|
||||
user: PropTypes.object,
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
theme: PropTypes.string,
|
||||
editOmnichannelContact: PropTypes.array,
|
||||
editLivechatRoomCustomfields: PropTypes.array
|
||||
};
|
||||
LivechatEditView.navigationOptions = {
|
||||
title: I18n.t('Edit')
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
server: state.server.server,
|
||||
user: getUserSelector(state),
|
||||
editOmnichannelContact: state.permissions['edit-omnichannel-contact'],
|
Loading…
Reference in New Issue