[IMPROVE] Support for tag request close when is obligatory to close Omnichannel's Channel (#4353)

* [IMPROVE] Support for tag request close

* clean up

* refactor isObrigatory and value

* fix obligatory

* removed onSubmitEditing

* create closelivechatview

* Refactor Close Omnichannel from ActionSheet to a new Screen

* Fix navigation

* fix screen title

* back to before actionsheetwithinput

* added string in pt-br

* refactor canSubmit

* fix the method closeRoom

* comment of server's version

* some tweaks

* Update app/lib/services/restApi.ts

Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com>
This commit is contained in:
Reinaldo Neto 2022-07-15 17:06:27 -03:00 committed by GitHub
parent 08c368c41e
commit fe7722ca10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 226 additions and 77 deletions

View File

@ -1,24 +0,0 @@
import React from 'react';
import ActionSheetContentWithInputAndSubmit from '../../../../containers/ActionSheet/ActionSheetContentWithInputAndSubmit';
import I18n from '../../../../i18n';
const CloseLivechatSheet = ({
onSubmit = () => {},
onCancel = () => {}
}: {
onSubmit: (comment: string) => void;
onCancel: () => void;
}) => (
<ActionSheetContentWithInputAndSubmit
title={I18n.t('Closing_chat')}
description={I18n.t('Please_add_a_comment')}
onCancel={onCancel}
onSubmit={onSubmit}
testID='room-actions-view-close-livechat'
placeholder=''
secureTextEntry={false}
/>
);
export default CloseLivechatSheet;

View File

@ -840,5 +840,7 @@
"Show_badge_for_mentions": "Show badge for mentions", "Show_badge_for_mentions": "Show badge for mentions",
"Show_badge_for_mentions_Info": "Display badge for direct mentions only", "Show_badge_for_mentions_Info": "Display badge for direct mentions only",
"error-init-video-conf": "Error starting video call", "error-init-video-conf": "Error starting video call",
"totp-invalid": "Code or password invalid" "totp-invalid": "Code or password invalid",
"Close_Chat": "Close Chat",
"Select_tags": "Select tags"
} }

View File

@ -793,5 +793,7 @@
"Mark_as_unread_Info": "Mostrar sala como não lida quando houver mensagens não lidas", "Mark_as_unread_Info": "Mostrar sala como não lida quando houver mensagens não lidas",
"Show_badge_for_mentions": "Mostrar contador para menções", "Show_badge_for_mentions": "Mostrar contador para menções",
"Show_badge_for_mentions_Info": "Mostrar contador somente para menções diretas", "Show_badge_for_mentions_Info": "Mostrar contador somente para menções diretas",
"totp-invalid": "Código ou senha inválida" "totp-invalid": "Código ou senha inválida",
"Close_Chat": "Fechar Conversa",
"Select_tags": "Selecionar tag(s)"
} }

View File

@ -7,14 +7,16 @@ import log from './log';
export const closeLivechat = async ({ export const closeLivechat = async ({
rid, rid,
comment, comment,
isMasterDetail isMasterDetail,
tags
}: { }: {
rid: string; rid: string;
isMasterDetail: boolean; isMasterDetail: boolean;
comment?: string; comment?: string;
tags?: string[];
}) => { }) => {
try { try {
await Services.closeLivechat(rid, comment); await Services.closeLivechat(rid, comment, tags);
if (isMasterDetail) { if (isMasterDetail) {
Navigation.navigate('DrawerNavigator'); Navigation.navigate('DrawerNavigator');
} else { } else {

View File

@ -18,6 +18,7 @@ import { getDeviceToken } from '../notifications';
import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods'; import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods';
import sdk from './sdk'; import sdk from './sdk';
import { compareServerVersion, getBundleId, isIOS } from '../methods/helpers'; import { compareServerVersion, getBundleId, isIOS } from '../methods/helpers';
import { ILivechatTag } from '../../definitions/ILivechatTag';
export const createChannel = ({ export const createChannel = ({
name, name,
@ -347,9 +348,15 @@ export const getTeamListRoom = ({
return sdk.get('teams.listRooms', params); return sdk.get('teams.listRooms', params);
}; };
export const closeLivechat = (rid: string, comment?: string) => export const closeLivechat = (rid: string, comment?: string, tags?: string[]) => {
// RC 3.2.0
let params;
if (tags && tags?.length) {
params = { tags };
}
// RC 0.29.0 // RC 0.29.0
sdk.methodCallWrapper('livechat:closeRoom', rid, comment, { clientAction: true }); return sdk.methodCallWrapper('livechat:closeRoom', rid, comment, { clientAction: true, ...params });
};
export const editLivechat = (userData: TParams, roomData: TParams): Promise<{ error?: string }> => export const editLivechat = (userData: TParams, roomData: TParams): Promise<{ error?: string }> =>
// RC 0.55.0 // RC 0.55.0
@ -398,13 +405,7 @@ export const getRoutingConfig = (): Promise<{
// RC 2.0.0 // RC 2.0.0
sdk.methodCallWrapper('livechat:getRoutingConfig'); sdk.methodCallWrapper('livechat:getRoutingConfig');
export const getTagsList = (): Promise< export const getTagsList = (): Promise<ILivechatTag[]> =>
{
_id: string;
name: string;
departments: string[];
}[]
> =>
// RC 2.0.0 // RC 2.0.0
sdk.methodCallWrapper('livechat:getTagsList'); sdk.methodCallWrapper('livechat:getTagsList');

View File

@ -22,6 +22,7 @@ import AutoTranslateView from '../views/AutoTranslateView';
import DirectoryView from '../views/DirectoryView'; import DirectoryView from '../views/DirectoryView';
import NotificationPrefView from '../views/NotificationPreferencesView'; import NotificationPrefView from '../views/NotificationPreferencesView';
import ForwardLivechatView from '../views/ForwardLivechatView'; import ForwardLivechatView from '../views/ForwardLivechatView';
import CloseLivechatView from '../views/CloseLivechatView';
import LivechatEditView from '../views/LivechatEditView'; import LivechatEditView from '../views/LivechatEditView';
import PickerView from '../views/PickerView'; import PickerView from '../views/PickerView';
import ThreadMessagesView from '../views/ThreadMessagesView'; import ThreadMessagesView from '../views/ThreadMessagesView';
@ -116,6 +117,7 @@ const ChatsStackNavigator = () => {
options={NotificationPrefView.navigationOptions} options={NotificationPrefView.navigationOptions}
/> />
<ChatsStack.Screen name='ForwardLivechatView' component={ForwardLivechatView} /> <ChatsStack.Screen name='ForwardLivechatView' component={ForwardLivechatView} />
<ChatsStack.Screen name='CloseLivechatView' component={CloseLivechatView} />
<ChatsStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} /> <ChatsStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} />
<ChatsStack.Screen name='PickerView' component={PickerView} options={PickerView.navigationOptions} /> <ChatsStack.Screen name='PickerView' component={PickerView} options={PickerView.navigationOptions} />
<ChatsStack.Screen <ChatsStack.Screen

View File

@ -27,6 +27,7 @@ import AutoTranslateView from '../../views/AutoTranslateView';
import DirectoryView from '../../views/DirectoryView'; import DirectoryView from '../../views/DirectoryView';
import NotificationPrefView from '../../views/NotificationPreferencesView'; import NotificationPrefView from '../../views/NotificationPreferencesView';
import ForwardLivechatView from '../../views/ForwardLivechatView'; import ForwardLivechatView from '../../views/ForwardLivechatView';
import CloseLivechatView from '../../views/CloseLivechatView';
import CannedResponsesListView from '../../views/CannedResponsesListView'; import CannedResponsesListView from '../../views/CannedResponsesListView';
import CannedResponseDetail from '../../views/CannedResponseDetail'; import CannedResponseDetail from '../../views/CannedResponseDetail';
import LivechatEditView from '../../views/LivechatEditView'; import LivechatEditView from '../../views/LivechatEditView';
@ -156,6 +157,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
options={NotificationPrefView.navigationOptions} options={NotificationPrefView.navigationOptions}
/> />
<ModalStack.Screen name='ForwardLivechatView' component={ForwardLivechatView} /> <ModalStack.Screen name='ForwardLivechatView' component={ForwardLivechatView} />
<ModalStack.Screen name='CloseLivechatView' component={CloseLivechatView} />
<ModalStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} /> <ModalStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
<ModalStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} /> <ModalStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
<ModalStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} /> <ModalStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} />

View File

@ -4,6 +4,8 @@ import { NavigatorScreenParams } from '@react-navigation/core';
import { IAttachment } from '../../definitions/IAttachment'; import { IAttachment } from '../../definitions/IAttachment';
import { IMessage } from '../../definitions/IMessage'; import { IMessage } from '../../definitions/IMessage';
import { ISubscription, SubscriptionType, TSubscriptionModel } from '../../definitions/ISubscription'; import { ISubscription, SubscriptionType, TSubscriptionModel } from '../../definitions/ISubscription';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
import { ILivechatTag } from '../../definitions/ILivechatTag';
export type MasterDetailChatsStackParamList = { export type MasterDetailChatsStackParamList = {
RoomView: { RoomView: {
@ -111,6 +113,12 @@ export type ModalStackParamList = {
ForwardLivechatView: { ForwardLivechatView: {
rid: string; rid: string;
}; };
CloseLivechatView: {
rid: string;
departmentId?: string;
departmentInfo?: ILivechatDepartment;
tagsList?: ILivechatTag[];
};
CannedResponsesListView: { CannedResponsesListView: {
rid: string; rid: string;
}; };

View File

@ -11,6 +11,8 @@ import { ICannedResponse } from '../definitions/ICannedResponse';
import { TDataSelect } from '../definitions/IDataSelect'; import { TDataSelect } from '../definitions/IDataSelect';
import { ModalStackParamList } from './MasterDetailStack/types'; import { ModalStackParamList } from './MasterDetailStack/types';
import { TThreadModel } from '../definitions'; import { TThreadModel } from '../definitions';
import { ILivechatDepartment } from '../definitions/ILivechatDepartment';
import { ILivechatTag } from '../definitions/ILivechatTag';
export type ChatsStackParamList = { export type ChatsStackParamList = {
ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>; ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>;
@ -114,6 +116,12 @@ export type ChatsStackParamList = {
ForwardLivechatView: { ForwardLivechatView: {
rid: string; rid: string;
}; };
CloseLivechatView: {
rid: string;
departmentId?: string;
departmentInfo?: ILivechatDepartment;
tagsList?: ILivechatTag[];
};
LivechatEditView: { LivechatEditView: {
room: ISubscription; room: ISubscription;
roomUser: any; // TODO: Change roomUser: any; // TODO: Change

View File

@ -0,0 +1,122 @@
import React, { useState, useLayoutEffect } from 'react';
import { StyleSheet, ScrollView, Text } from 'react-native';
import { BlockContext } from '@rocket.chat/ui-kit';
import { IBaseScreen } from '../definitions';
import I18n from '../i18n';
import { ChatsStackParamList } from '../stacks/types';
import { useTheme } from '../theme';
import KeyboardView from '../containers/KeyboardView';
import SafeAreaView from '../containers/SafeAreaView';
import { FormTextInput } from '../containers/TextInput';
import Button from '../containers/Button';
import { useAppSelector } from '../lib/hooks';
import sharedStyles from './Styles';
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
import { MultiSelect } from '../containers/UIKit/MultiSelect';
import { closeLivechat } from '../lib/methods/helpers/closeLivechat';
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16
},
subtitleText: {
marginBottom: 10,
fontSize: 14,
...sharedStyles.textSemibold
},
buttonMarginVertical: { marginVertical: 20 }
});
const CloseLivechatView = ({ navigation, route }: IBaseScreen<ChatsStackParamList, 'CloseLivechatView'>) => {
const rid = route.params?.rid;
const departmentInfo = route.params?.departmentInfo;
const tagsList = route.params?.tagsList;
const requestTags = departmentInfo?.requestTagBeforeClosingChat;
const [inputValue, setInputValue] = useState('');
const [tagParamSelected, setTagParamSelected] = useState<string[]>([]);
const { colors } = useTheme();
const { isMasterDetail, livechatRequestComment } = useAppSelector(state => ({
isMasterDetail: state.app.isMasterDetail,
livechatRequestComment: state.settings.Livechat_request_comment_when_closing_conversation as boolean
}));
useLayoutEffect(() => {
navigation.setOptions({
title: I18n.t('Close_Chat')
});
}, [navigation]);
const canSubmit = () => {
if (!requestTags && !livechatRequestComment) {
return true;
}
if (requestTags && tagParamSelected.length > 0 && !livechatRequestComment) {
return true;
}
if (livechatRequestComment && !!inputValue && !requestTags) {
return true;
}
if (livechatRequestComment && requestTags && tagParamSelected.length > 0 && !!inputValue) {
return true;
}
return false;
};
const submit = () => {
closeLivechat({ rid, isMasterDetail, comment: inputValue, tags: tagParamSelected });
};
return (
<KeyboardView
style={{ backgroundColor: colors.auxiliaryBackground }}
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}>
<ScrollView {...scrollPersistTaps} style={styles.container}>
<SafeAreaView>
<FormTextInput
label={I18n.t('Please_add_a_comment')}
defaultValue={''}
onChangeText={text => setInputValue(text)}
onSubmitEditing={() => {
if (canSubmit()) {
submit();
}
}}
/>
{requestTags ? (
<>
<Text style={[styles.subtitleText, { color: colors.titleText }]}>{I18n.t('Tags')}</Text>
<MultiSelect
options={tagsList?.map(({ name }) => ({ text: { text: name }, value: name }))}
onChange={({ value }: { value: string[] }) => {
setTagParamSelected(value);
}}
placeholder={{ text: I18n.t('Select_tags') }}
value={tagParamSelected}
context={BlockContext.FORM}
multiselect
inputStyle={{ borderColor: colors.separatorColor, borderWidth: 2 }}
/>
</>
) : null}
<Button
title={I18n.t('Close')}
onPress={submit}
disabled={!canSubmit()}
backgroundColor={colors.dangerColor}
type='primary'
style={styles.buttonMarginVertical}
/>
</SafeAreaView>
</ScrollView>
</KeyboardView>
);
};
export default CloseLivechatView;

View File

@ -46,10 +46,11 @@ import {
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription'; import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
import { IActionSheetProvider, withActionSheet } from '../../containers/ActionSheet'; import { IActionSheetProvider, withActionSheet } from '../../containers/ActionSheet';
import CloseLivechatSheet from '../../ee/omnichannel/containers/CloseLivechatSheet';
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types'; import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
import { closeLivechat } from '../../lib/methods/helpers/closeLivechat'; import { closeLivechat } from '../../lib/methods/helpers/closeLivechat';
import { videoConfStartAndJoin } from '../../lib/methods/videoConf'; import { videoConfStartAndJoin } from '../../lib/methods/videoConf';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
import { ILivechatTag } from '../../definitions/ILivechatTag';
interface IOnPressTouch { interface IOnPressTouch {
<T extends keyof ChatsStackParamList>(item: { route?: T; params?: ChatsStackParamList[T]; event?: Function }): void; <T extends keyof ChatsStackParamList>(item: { route?: T; params?: ChatsStackParamList[T]; event?: Function }): void;
@ -376,29 +377,31 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
); );
}; };
closeLivechat = () => { closeLivechat = async () => {
const { const {
room: { rid } room: { rid, departmentId }
} = this.state; } = this.state;
const { livechatRequestComment, showActionSheet, hideActionSheet, isMasterDetail } = this.props; const { livechatRequestComment, isMasterDetail, navigation } = this.props;
let departmentInfo: ILivechatDepartment | undefined;
let tagsList: ILivechatTag[] | undefined;
if (!livechatRequestComment) { if (departmentId) {
const result = await Services.getDepartmentInfo(departmentId);
if (result.success) {
departmentInfo = result.department as ILivechatDepartment;
}
}
if (departmentInfo?.requestTagBeforeClosingChat) {
tagsList = await Services.getTagsList();
}
if (!livechatRequestComment && !departmentInfo?.requestTagBeforeClosingChat) {
const comment = I18n.t('Chat_closed_by_agent'); const comment = I18n.t('Chat_closed_by_agent');
return closeLivechat({ rid, isMasterDetail, comment }); return closeLivechat({ rid, isMasterDetail, comment });
} }
showActionSheet({ navigation.navigate('CloseLivechatView', { rid, departmentId, departmentInfo, tagsList });
children: (
<CloseLivechatSheet
onSubmit={(comment: string) => {
hideActionSheet();
closeLivechat({ rid, isMasterDetail, comment });
}}
onCancel={() => hideActionSheet()}
/>
),
headerHeight: 225
});
}; };
placeOnHoldLivechat = () => { placeOnHoldLivechat = () => {

View File

@ -5,6 +5,7 @@ import { Observable, Subscription } from 'rxjs';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { ILivechatTag } from '../../definitions/ILivechatTag';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import database from '../../lib/database'; import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
@ -17,7 +18,8 @@ import i18n from '../../i18n';
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers'; import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
import { onHoldLivechat, returnLivechat } from '../../lib/services/restApi'; import { onHoldLivechat, returnLivechat } from '../../lib/services/restApi';
import { closeLivechat as closeLivechatService } from '../../lib/methods/helpers/closeLivechat'; import { closeLivechat as closeLivechatService } from '../../lib/methods/helpers/closeLivechat';
import CloseLivechatSheet from '../../ee/omnichannel/containers/CloseLivechatSheet'; import { Services } from '../../lib/services';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
interface IRightButtonsProps extends IActionSheetProvider { interface IRightButtonsProps extends IActionSheetProvider {
userId?: string; userId?: string;
@ -40,6 +42,7 @@ interface IRightButtonsProps extends IActionSheetProvider {
canPlaceLivechatOnHold: boolean; canPlaceLivechatOnHold: boolean;
}; };
livechatRequestComment: boolean; livechatRequestComment: boolean;
departmentId?: string;
} }
interface IRigthButtonsState { interface IRigthButtonsState {
@ -214,35 +217,41 @@ class RightButtonsContainer extends Component<IRightButtonsProps, IRigthButtonsS
}); });
}; };
closeLivechat = () => { closeLivechat = async () => {
const { rid, livechatRequestComment, showActionSheet, hideActionSheet, isMasterDetail } = this.props; const { rid, departmentId } = this.props;
const { livechatRequestComment, isMasterDetail, navigation } = this.props;
let departmentInfo: ILivechatDepartment | undefined;
let tagsList: ILivechatTag[] | undefined;
hideActionSheet(); if (departmentId) {
const result = await Services.getDepartmentInfo(departmentId);
if (result.success) {
departmentInfo = result.department as ILivechatDepartment;
}
}
setTimeout(() => { if (departmentInfo?.requestTagBeforeClosingChat) {
if (!livechatRequestComment) { tagsList = await Services.getTagsList();
}
if (!livechatRequestComment && !departmentInfo?.requestTagBeforeClosingChat) {
const comment = i18n.t('Chat_closed_by_agent'); const comment = i18n.t('Chat_closed_by_agent');
return closeLivechatService({ rid, isMasterDetail, comment }); return closeLivechatService({ rid, isMasterDetail, comment });
} }
showActionSheet({ if (isMasterDetail) {
children: ( navigation.navigate('ModalStackNavigator', {
<CloseLivechatSheet screen: 'CloseLivechatView',
onSubmit={(comment: string) => { params: { rid, departmentId, departmentInfo, tagsList }
hideActionSheet();
closeLivechatService({ rid, isMasterDetail, comment });
}}
onCancel={() => hideActionSheet()}
/>
),
headerHeight: 225
}); });
}, 300); } else {
navigation.navigate('CloseLivechatView', { rid, departmentId, departmentInfo, tagsList });
}
}; };
showMoreActions = () => { showMoreActions = () => {
logEvent(events.ROOM_SHOW_MORE_ACTIONS); logEvent(events.ROOM_SHOW_MORE_ACTIONS);
const { showActionSheet, rid, navigation, omnichannelPermissions } = this.props; const { showActionSheet, rid, navigation, omnichannelPermissions, isMasterDetail } = this.props;
const options = [] as TActionSheetOptionsItem[]; const options = [] as TActionSheetOptionsItem[];
if (omnichannelPermissions.canPlaceLivechatOnHold) { if (omnichannelPermissions.canPlaceLivechatOnHold) {
@ -257,7 +266,16 @@ class RightButtonsContainer extends Component<IRightButtonsProps, IRigthButtonsS
options.push({ options.push({
title: i18n.t('Forward_Chat'), title: i18n.t('Forward_Chat'),
icon: 'chat-forward', icon: 'chat-forward',
onPress: () => navigation.navigate('ForwardLivechatView', { rid }) onPress: () => {
if (isMasterDetail) {
navigation.navigate('ModalStackNavigator', {
screen: 'ForwardLivechatView',
params: { rid }
});
} else {
navigation.navigate('ForwardLivechatView', { rid });
}
}
}); });
} }

View File

@ -559,6 +559,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
let avatar: string | undefined; let avatar: string | undefined;
let visitor: IVisitor | undefined; let visitor: IVisitor | undefined;
let sourceType: IOmnichannelSource | undefined; let sourceType: IOmnichannelSource | undefined;
let departmentId: string | undefined;
if ('id' in room) { if ('id' in room) {
subtitle = room.topic; subtitle = room.topic;
t = room.t; t = room.t;
@ -568,6 +569,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
({ id: userId, token } = user); ({ id: userId, token } = user);
avatar = room.name; avatar = room.name;
visitor = room.visitor; visitor = room.visitor;
departmentId = room.departmentId;
} }
if ('source' in room) { if ('source' in room) {
@ -632,6 +634,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
encrypted={encrypted} encrypted={encrypted}
navigation={navigation} navigation={navigation}
toggleFollowThread={this.toggleFollowThread} toggleFollowThread={this.toggleFollowThread}
departmentId={departmentId}
/> />
) )
}); });