[NEW] Call to Action buttons for Omnichannel (#4060)
* add: kebab and bottom sheet for omnichannel * update: `RoomActionsView` * chore: add canned responses to MessageBox * chore: add omnichannel permissions to RightButtons chore: missing changes to previous commit on RoomView * chore: make icon available on first re-render after joining the room * refactor: omnichannel permissions logic outside RightButtons * refactor: extract omnichannel permissions' logic from RoomActionsView * fix: omnichannel permissions on RightButton component * add: omnichannelPermissions to RoomActions route props * update: RoomView * remove: setOmnichannelPermissions function * refactor: `omnichannelPermissions` to object * refactor: extract `on-hold` Omnichannel to `RoomView` * add: `canPlaceLivechatOnHold` to `Omnichannel`s actions * update: type for `showActionSheet` * update: canned responses permission on * fix: place on-hold permission not updating properly * update: validation for on-hold remove: unused variable * remove: unnecessary param * update: MessageBox * fix: Omnichannel permissions on taking chat * undo: comment on Reactotron * Fix place chat on-hold * fix setOptions in messageBox * resolve lint * change return to return to waiting line * remove joined * fix console.log * fix kebab button * fix subscription to room outside of db Co-authored-by: Reinaldo Neto <reinaldonetof@hotmail.com> Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>
This commit is contained in:
parent
d47320733a
commit
20a03611a7
|
@ -46,12 +46,22 @@ import { withActionSheet } from '../ActionSheet';
|
||||||
import { sanitizeLikeString } from '../../lib/database/utils';
|
import { sanitizeLikeString } from '../../lib/database/utils';
|
||||||
import { CustomIcon } from '../CustomIcon';
|
import { CustomIcon } from '../CustomIcon';
|
||||||
import { forceJpgExtension } from './forceJpgExtension';
|
import { forceJpgExtension } from './forceJpgExtension';
|
||||||
import { IBaseScreen, IPreviewItem, IUser, TGetCustomEmoji, TSubscriptionModel, TThreadModel, IMessage } from '../../definitions';
|
import {
|
||||||
|
IApplicationState,
|
||||||
|
IBaseScreen,
|
||||||
|
IPreviewItem,
|
||||||
|
IUser,
|
||||||
|
TGetCustomEmoji,
|
||||||
|
TSubscriptionModel,
|
||||||
|
TThreadModel,
|
||||||
|
IMessage
|
||||||
|
} from '../../definitions';
|
||||||
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||||
import { getPermalinkMessage, search, sendFileMessage } from '../../lib/methods';
|
import { getPermalinkMessage, search, sendFileMessage } from '../../lib/methods';
|
||||||
import { hasPermission, debounce, isAndroid, isTablet } from '../../lib/methods/helpers';
|
import { hasPermission, debounce, isAndroid, isTablet } from '../../lib/methods/helpers';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
|
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
require('./EmojiKeyboard');
|
require('./EmojiKeyboard');
|
||||||
|
@ -75,7 +85,7 @@ const videoPickerConfig: Options = {
|
||||||
mediaType: 'video'
|
mediaType: 'video'
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IMessageBoxProps extends IBaseScreen<MasterDetailInsideStackParamList, any> {
|
export interface IMessageBoxProps extends IBaseScreen<ChatsStackParamList & MasterDetailInsideStackParamList, any> {
|
||||||
rid: string;
|
rid: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
message: IMessage;
|
message: IMessage;
|
||||||
|
@ -107,6 +117,7 @@ export interface IMessageBoxProps extends IBaseScreen<MasterDetailInsideStackPar
|
||||||
usedCannedResponse: string;
|
usedCannedResponse: string;
|
||||||
uploadFilePermission: string[];
|
uploadFilePermission: string[];
|
||||||
serverVersion: string;
|
serverVersion: string;
|
||||||
|
goToCannedResponses: () => void | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageBoxState {
|
interface IMessageBoxState {
|
||||||
|
@ -305,7 +316,17 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
permissionToUpload
|
permissionToUpload
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse, uploadFilePermission } = this.props;
|
const {
|
||||||
|
roomType,
|
||||||
|
replying,
|
||||||
|
editing,
|
||||||
|
isFocused,
|
||||||
|
message,
|
||||||
|
theme,
|
||||||
|
usedCannedResponse,
|
||||||
|
uploadFilePermission,
|
||||||
|
goToCannedResponses
|
||||||
|
} = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -357,12 +378,15 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextProps.goToCannedResponses !== goToCannedResponses) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IMessageBoxProps) {
|
componentDidUpdate(prevProps: IMessageBoxProps) {
|
||||||
const { uploadFilePermission } = this.props;
|
const { uploadFilePermission, goToCannedResponses } = this.props;
|
||||||
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission)) {
|
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission) || prevProps.goToCannedResponses !== goToCannedResponses) {
|
||||||
this.setOptions();
|
this.setOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,9 +807,16 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
showMessageBoxActions = () => {
|
showMessageBoxActions = () => {
|
||||||
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
||||||
const { permissionToUpload } = this.state;
|
const { permissionToUpload } = this.state;
|
||||||
const { showActionSheet } = this.props;
|
const { showActionSheet, goToCannedResponses } = this.props;
|
||||||
|
|
||||||
const options = [];
|
const options = [];
|
||||||
|
if (goToCannedResponses) {
|
||||||
|
options.push({
|
||||||
|
title: I18n.t('Canned_Responses'),
|
||||||
|
icon: 'canned-response',
|
||||||
|
onPress: () => goToCannedResponses()
|
||||||
|
});
|
||||||
|
}
|
||||||
if (permissionToUpload) {
|
if (permissionToUpload) {
|
||||||
options.push(
|
options.push(
|
||||||
{
|
{
|
||||||
|
@ -1170,7 +1201,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
threadsEnabled: state.settings.Threads_enabled,
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
|
|
|
@ -198,7 +198,7 @@ export interface IServerRoom extends IRocketChatRecord {
|
||||||
unread?: number;
|
unread?: number;
|
||||||
alert?: boolean;
|
alert?: boolean;
|
||||||
hideUnreadStatus?: boolean;
|
hideUnreadStatus?: boolean;
|
||||||
|
status?: string;
|
||||||
sysMes?: string[];
|
sysMes?: string[];
|
||||||
muted?: string[];
|
muted?: string[];
|
||||||
unmuted?: string[];
|
unmuted?: string[];
|
||||||
|
|
|
@ -421,7 +421,6 @@
|
||||||
"Reset_password": "إعادة تعيين كلمة المرور",
|
"Reset_password": "إعادة تعيين كلمة المرور",
|
||||||
"resetting_password": "إعادة تعيين كلمة المرور",
|
"resetting_password": "إعادة تعيين كلمة المرور",
|
||||||
"RESET": "إعادة",
|
"RESET": "إعادة",
|
||||||
"Return": "العودة",
|
|
||||||
"Review_app_title": "هل أنت مستمتع بهذا التطبيق؟",
|
"Review_app_title": "هل أنت مستمتع بهذا التطبيق؟",
|
||||||
"Review_app_desc": "قم بإعطائنا 5 نجوم {{store}}",
|
"Review_app_desc": "قم بإعطائنا 5 نجوم {{store}}",
|
||||||
"Review_app_yes": "أكيد!",
|
"Review_app_yes": "أكيد!",
|
||||||
|
|
|
@ -427,7 +427,6 @@
|
||||||
"Reset_password": "Passwort zurücksetzen",
|
"Reset_password": "Passwort zurücksetzen",
|
||||||
"resetting_password": "Passwort zurücksetzen",
|
"resetting_password": "Passwort zurücksetzen",
|
||||||
"RESET": "ZURÜCKSETZEN",
|
"RESET": "ZURÜCKSETZEN",
|
||||||
"Return": "Zurück",
|
|
||||||
"Review_app_title": "Gefällt Ihnen diese App?",
|
"Review_app_title": "Gefällt Ihnen diese App?",
|
||||||
"Review_app_desc": "Geben Sie uns 5 Sterne im {{store}}",
|
"Review_app_desc": "Geben Sie uns 5 Sterne im {{store}}",
|
||||||
"Review_app_yes": "Sicher!",
|
"Review_app_yes": "Sicher!",
|
||||||
|
|
|
@ -431,7 +431,7 @@
|
||||||
"Reset_password": "Reset password",
|
"Reset_password": "Reset password",
|
||||||
"resetting_password": "resetting password",
|
"resetting_password": "resetting password",
|
||||||
"RESET": "RESET",
|
"RESET": "RESET",
|
||||||
"Return": "Return",
|
"Return_to_waiting_line": "Return to waiting line",
|
||||||
"Review_app_title": "Are you enjoying this app?",
|
"Review_app_title": "Are you enjoying this app?",
|
||||||
"Review_app_desc": "Give us 5 stars on {{store}}",
|
"Review_app_desc": "Give us 5 stars on {{store}}",
|
||||||
"Review_app_yes": "Sure!",
|
"Review_app_yes": "Sure!",
|
||||||
|
|
|
@ -427,7 +427,6 @@
|
||||||
"Reset_password": "Réinitialiser le mot de passe",
|
"Reset_password": "Réinitialiser le mot de passe",
|
||||||
"resetting_password": "réinitialisation du mot de passe",
|
"resetting_password": "réinitialisation du mot de passe",
|
||||||
"RESET": "RÉINITIALISER",
|
"RESET": "RÉINITIALISER",
|
||||||
"Return": "Retour",
|
|
||||||
"Review_app_title": "Appréciez-vous cette application ?",
|
"Review_app_title": "Appréciez-vous cette application ?",
|
||||||
"Review_app_desc": "Donnez-nous 5 étoiles sur {{store}}",
|
"Review_app_desc": "Donnez-nous 5 étoiles sur {{store}}",
|
||||||
"Review_app_yes": "Bien sûr !",
|
"Review_app_yes": "Bien sûr !",
|
||||||
|
|
|
@ -416,7 +416,6 @@
|
||||||
"Reset_password": "Ripristina password",
|
"Reset_password": "Ripristina password",
|
||||||
"resetting_password": "ripristinando password",
|
"resetting_password": "ripristinando password",
|
||||||
"RESET": "RIPRISTINA",
|
"RESET": "RIPRISTINA",
|
||||||
"Return": "Ritorno",
|
|
||||||
"Review_app_title": "Ti piace questa app?",
|
"Review_app_title": "Ti piace questa app?",
|
||||||
"Review_app_desc": "Dacci 5 stesse su {{store}}",
|
"Review_app_desc": "Dacci 5 stesse su {{store}}",
|
||||||
"Review_app_yes": "Certo!",
|
"Review_app_yes": "Certo!",
|
||||||
|
|
|
@ -427,7 +427,6 @@
|
||||||
"Reset_password": "Wachtwoord resetten",
|
"Reset_password": "Wachtwoord resetten",
|
||||||
"resetting_password": "wachtwoord aan het resetten",
|
"resetting_password": "wachtwoord aan het resetten",
|
||||||
"RESET": "RESET",
|
"RESET": "RESET",
|
||||||
"Return": "Terug",
|
|
||||||
"Review_app_title": "Geniet je van deze app?",
|
"Review_app_title": "Geniet je van deze app?",
|
||||||
"Review_app_desc": "Geef ons 5 sterren op {{store}}",
|
"Review_app_desc": "Geef ons 5 sterren op {{store}}",
|
||||||
"Review_app_yes": "Zeker!",
|
"Review_app_yes": "Zeker!",
|
||||||
|
|
|
@ -403,7 +403,6 @@
|
||||||
"Reset_password": "Resetar senha",
|
"Reset_password": "Resetar senha",
|
||||||
"resetting_password": "redefinindo senha",
|
"resetting_password": "redefinindo senha",
|
||||||
"RESET": "RESETAR",
|
"RESET": "RESETAR",
|
||||||
"Return": "Retornar",
|
|
||||||
"Review_app_title": "Você está gostando do app?",
|
"Review_app_title": "Você está gostando do app?",
|
||||||
"Review_app_desc": "Nos dê 5 estrelas na {{store}}",
|
"Review_app_desc": "Nos dê 5 estrelas na {{store}}",
|
||||||
"Review_app_yes": "Claro!",
|
"Review_app_yes": "Claro!",
|
||||||
|
|
|
@ -427,7 +427,6 @@
|
||||||
"Reset_password": "Сброс пароля",
|
"Reset_password": "Сброс пароля",
|
||||||
"resetting_password": "сброс пароля",
|
"resetting_password": "сброс пароля",
|
||||||
"RESET": "СБРОС",
|
"RESET": "СБРОС",
|
||||||
"Return": "Возврат",
|
|
||||||
"Review_app_title": "Нравится ли вам это приложение?",
|
"Review_app_title": "Нравится ли вам это приложение?",
|
||||||
"Review_app_desc": "Поставьте нам 5 звезд в {{store}}",
|
"Review_app_desc": "Поставьте нам 5 звезд в {{store}}",
|
||||||
"Review_app_yes": "Конечно!",
|
"Review_app_yes": "Конечно!",
|
||||||
|
|
|
@ -417,7 +417,6 @@
|
||||||
"Reset_password": "Şifre sıfırla",
|
"Reset_password": "Şifre sıfırla",
|
||||||
"resetting_password": "şifre sıfırlanıyor",
|
"resetting_password": "şifre sıfırlanıyor",
|
||||||
"RESET": "SIFIRLA",
|
"RESET": "SIFIRLA",
|
||||||
"Return": "Geri dön",
|
|
||||||
"Review_app_title": "Uygulama hoşunuza gitti mi?",
|
"Review_app_title": "Uygulama hoşunuza gitti mi?",
|
||||||
"Review_app_desc": "{{store}} üzerinde bize 5 yıldız verin",
|
"Review_app_desc": "{{store}} üzerinde bize 5 yıldız verin",
|
||||||
"Review_app_yes": "Elbette!",
|
"Review_app_yes": "Elbette!",
|
||||||
|
|
|
@ -414,7 +414,6 @@
|
||||||
"Reset_password": "重置密码",
|
"Reset_password": "重置密码",
|
||||||
"resetting_password": "正在重置密码",
|
"resetting_password": "正在重置密码",
|
||||||
"RESET": "重置",
|
"RESET": "重置",
|
||||||
"Return": "返回",
|
|
||||||
"Review_app_title": "对此 App 满意吗?",
|
"Review_app_title": "对此 App 满意吗?",
|
||||||
"Review_app_desc": "请在 {{store}} 给予我们 5 星好评",
|
"Review_app_desc": "请在 {{store}} 给予我们 5 星好评",
|
||||||
"Review_app_yes": "没问题",
|
"Review_app_yes": "没问题",
|
||||||
|
|
|
@ -416,7 +416,6 @@
|
||||||
"Reset_password": "重置密碼",
|
"Reset_password": "重置密碼",
|
||||||
"resetting_password": "正在重置密碼",
|
"resetting_password": "正在重置密碼",
|
||||||
"RESET": "重置",
|
"RESET": "重置",
|
||||||
"Return": "返回",
|
|
||||||
"Review_app_title": "對此 App 滿意嗎?",
|
"Review_app_title": "對此 App 滿意嗎?",
|
||||||
"Review_app_desc": "請在 {{store}} 給予我們 5 星好評",
|
"Review_app_desc": "請在 {{store}} 給予我們 5 星好評",
|
||||||
"Review_app_yes": "沒問題",
|
"Review_app_yes": "沒問題",
|
||||||
|
|
|
@ -193,6 +193,7 @@ export default {
|
||||||
ROOM_AUDIO_FINISH_F: 'room_audio_finish_f',
|
ROOM_AUDIO_FINISH_F: 'room_audio_finish_f',
|
||||||
ROOM_AUDIO_CANCEL: 'room_audio_cancel',
|
ROOM_AUDIO_CANCEL: 'room_audio_cancel',
|
||||||
ROOM_AUDIO_CANCEL_F: 'room_audio_cancel_f',
|
ROOM_AUDIO_CANCEL_F: 'room_audio_cancel_f',
|
||||||
|
ROOM_SHOW_MORE_ACTIONS: 'room_show_more_actions',
|
||||||
ROOM_SHOW_BOX_ACTIONS: 'room_show_box_actions',
|
ROOM_SHOW_BOX_ACTIONS: 'room_show_box_actions',
|
||||||
ROOM_BOX_ACTION_PHOTO: 'room_box_action_photo',
|
ROOM_BOX_ACTION_PHOTO: 'room_box_action_photo',
|
||||||
ROOM_BOX_ACTION_PHOTO_F: 'room_box_action_photo_f',
|
ROOM_BOX_ACTION_PHOTO_F: 'room_box_action_photo_f',
|
||||||
|
|
|
@ -32,6 +32,12 @@ export type ModalStackParamList = {
|
||||||
rid: string;
|
rid: string;
|
||||||
t: SubscriptionType;
|
t: SubscriptionType;
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
|
omnichannelPermissions?: {
|
||||||
|
canForwardGuest: boolean;
|
||||||
|
canReturnQueue: boolean;
|
||||||
|
canViewCannedResponse: boolean;
|
||||||
|
canPlaceLivechatOnHold: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
RoomInfoView: {
|
RoomInfoView: {
|
||||||
room: ISubscription;
|
room: ISubscription;
|
||||||
|
|
|
@ -34,6 +34,7 @@ export type ChatsStackParamList = {
|
||||||
jumpToThreadId?: string;
|
jumpToThreadId?: string;
|
||||||
roomUserId?: string | null;
|
roomUserId?: string | null;
|
||||||
usedCannedResponse?: string;
|
usedCannedResponse?: string;
|
||||||
|
status?: string;
|
||||||
}
|
}
|
||||||
| undefined; // Navigates back to RoomView already on stack
|
| undefined; // Navigates back to RoomView already on stack
|
||||||
RoomActionsView: {
|
RoomActionsView: {
|
||||||
|
@ -42,6 +43,12 @@ export type ChatsStackParamList = {
|
||||||
rid: string;
|
rid: string;
|
||||||
t: SubscriptionType;
|
t: SubscriptionType;
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
|
omnichannelPermissions?: {
|
||||||
|
canForwardGuest: boolean;
|
||||||
|
canReturnQueue: boolean;
|
||||||
|
canViewCannedResponse: boolean;
|
||||||
|
canPlaceLivechatOnHold: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
SelectListView: {
|
SelectListView: {
|
||||||
data?: TDataSelect[];
|
data?: TDataSelect[];
|
||||||
|
|
|
@ -64,12 +64,10 @@ interface IRoomActionsViewProps extends IBaseScreen<ChatsStackParamList, 'RoomAc
|
||||||
editRoomPermission?: string[];
|
editRoomPermission?: string[];
|
||||||
toggleRoomE2EEncryptionPermission?: string[];
|
toggleRoomE2EEncryptionPermission?: string[];
|
||||||
viewBroadcastMemberListPermission?: string[];
|
viewBroadcastMemberListPermission?: string[];
|
||||||
transferLivechatGuestPermission?: string[];
|
|
||||||
createTeamPermission?: string[];
|
createTeamPermission?: string[];
|
||||||
addTeamChannelPermission?: string[];
|
addTeamChannelPermission?: string[];
|
||||||
convertTeamPermission?: string[];
|
convertTeamPermission?: string[];
|
||||||
viewCannedResponsesPermission?: string[];
|
viewCannedResponsesPermission?: string[];
|
||||||
livechatAllowManualOnHold?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRoomActionsViewState {
|
interface IRoomActionsViewState {
|
||||||
|
@ -81,16 +79,11 @@ interface IRoomActionsViewState {
|
||||||
canAutoTranslate: boolean;
|
canAutoTranslate: boolean;
|
||||||
canAddUser: boolean;
|
canAddUser: boolean;
|
||||||
canInviteUser: boolean;
|
canInviteUser: boolean;
|
||||||
canForwardGuest: boolean;
|
|
||||||
canReturnQueue: boolean;
|
|
||||||
canEdit: boolean;
|
canEdit: boolean;
|
||||||
canToggleEncryption: boolean;
|
canToggleEncryption: boolean;
|
||||||
canCreateTeam: boolean;
|
canCreateTeam: boolean;
|
||||||
canAddChannelToTeam: boolean;
|
canAddChannelToTeam: boolean;
|
||||||
canConvertTeam: boolean;
|
canConvertTeam: boolean;
|
||||||
canViewCannedResponse: boolean;
|
|
||||||
canPlaceLivechatOnHold: boolean;
|
|
||||||
isOnHold: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomActionsViewState> {
|
class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomActionsViewState> {
|
||||||
|
@ -98,6 +91,12 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
private rid: string;
|
private rid: string;
|
||||||
private t: string;
|
private t: string;
|
||||||
private joined: boolean;
|
private joined: boolean;
|
||||||
|
private omnichannelPermissions?: {
|
||||||
|
canForwardGuest: boolean;
|
||||||
|
canReturnQueue: boolean;
|
||||||
|
canViewCannedResponse: boolean;
|
||||||
|
canPlaceLivechatOnHold: boolean;
|
||||||
|
};
|
||||||
private roomObservable?: Observable<TSubscriptionModel>;
|
private roomObservable?: Observable<TSubscriptionModel>;
|
||||||
private subscription?: Subscription;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
|
@ -122,6 +121,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
this.rid = props.route.params?.rid;
|
this.rid = props.route.params?.rid;
|
||||||
this.t = props.route.params?.t;
|
this.t = props.route.params?.t;
|
||||||
this.joined = props.route.params?.joined;
|
this.joined = props.route.params?.joined;
|
||||||
|
this.omnichannelPermissions = props.route.params?.omnichannelPermissions;
|
||||||
this.state = {
|
this.state = {
|
||||||
room: room || { rid: this.rid, t: this.t },
|
room: room || { rid: this.rid, t: this.t },
|
||||||
membersCount: 0,
|
membersCount: 0,
|
||||||
|
@ -131,22 +131,17 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
canAutoTranslate: false,
|
canAutoTranslate: false,
|
||||||
canAddUser: false,
|
canAddUser: false,
|
||||||
canInviteUser: false,
|
canInviteUser: false,
|
||||||
canForwardGuest: false,
|
|
||||||
canReturnQueue: false,
|
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
canToggleEncryption: false,
|
canToggleEncryption: false,
|
||||||
canCreateTeam: false,
|
canCreateTeam: false,
|
||||||
canAddChannelToTeam: false,
|
canAddChannelToTeam: false,
|
||||||
canConvertTeam: false,
|
canConvertTeam: false
|
||||||
canViewCannedResponse: false,
|
|
||||||
canPlaceLivechatOnHold: false,
|
|
||||||
isOnHold: false
|
|
||||||
};
|
};
|
||||||
if (room && room.observe && room.rid) {
|
if (room && room.observe && room.rid) {
|
||||||
this.roomObservable = room.observe();
|
this.roomObservable = room.observe();
|
||||||
this.subscription = this.roomObservable.subscribe(changes => {
|
this.subscription = this.roomObservable.subscribe(changes => {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ room: changes, isOnHold: !!changes?.onHold });
|
this.setState({ room: changes });
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.state.room = changes;
|
this.state.room = changes;
|
||||||
|
@ -214,28 +209,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
canAddChannelToTeam,
|
canAddChannelToTeam,
|
||||||
canConvertTeam
|
canConvertTeam
|
||||||
});
|
});
|
||||||
|
|
||||||
// livechat permissions
|
|
||||||
if (room.t === 'l') {
|
|
||||||
const canForwardGuest = await this.canForwardGuest();
|
|
||||||
const canReturnQueue = await this.canReturnQueue();
|
|
||||||
const canViewCannedResponse = await this.canViewCannedResponse();
|
|
||||||
const canPlaceLivechatOnHold = this.canPlaceLivechatOnHold();
|
|
||||||
this.setState({ canForwardGuest, canReturnQueue, canViewCannedResponse, canPlaceLivechatOnHold });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IRoomActionsViewProps, prevState: IRoomActionsViewState) {
|
|
||||||
const { livechatAllowManualOnHold } = this.props;
|
|
||||||
const { room, isOnHold } = this.state;
|
|
||||||
|
|
||||||
if (
|
|
||||||
room.t === 'l' &&
|
|
||||||
(isOnHold !== prevState.isOnHold || prevProps.livechatAllowManualOnHold !== livechatAllowManualOnHold)
|
|
||||||
) {
|
|
||||||
const canPlaceLivechatOnHold = this.canPlaceLivechatOnHold();
|
|
||||||
this.setState({ canPlaceLivechatOnHold });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,38 +345,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
canForwardGuest = async () => {
|
|
||||||
const { room } = this.state;
|
|
||||||
const { transferLivechatGuestPermission } = this.props;
|
|
||||||
const { rid } = room;
|
|
||||||
const permissions = await hasPermission([transferLivechatGuestPermission], rid);
|
|
||||||
return permissions[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
canViewCannedResponse = async () => {
|
|
||||||
const { room } = this.state;
|
|
||||||
const { viewCannedResponsesPermission } = this.props;
|
|
||||||
const { rid } = room;
|
|
||||||
const permissions = await hasPermission([viewCannedResponsesPermission], rid);
|
|
||||||
return permissions[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
canPlaceLivechatOnHold = (): boolean => {
|
|
||||||
const { livechatAllowManualOnHold } = this.props;
|
|
||||||
const { room } = this.state;
|
|
||||||
|
|
||||||
return !!(livechatAllowManualOnHold && !room?.lastMessage?.token && room?.lastMessage?.u && !room.onHold);
|
|
||||||
};
|
|
||||||
|
|
||||||
canReturnQueue = async () => {
|
|
||||||
try {
|
|
||||||
const { returnQueue } = await Services.getRoutingConfig();
|
|
||||||
return returnQueue;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderEncryptedSwitch = () => {
|
renderEncryptedSwitch = () => {
|
||||||
const { room, canToggleEncryption, canEdit } = this.state;
|
const { room, canToggleEncryption, canEdit } = this.state;
|
||||||
const { encrypted } = room;
|
const { encrypted } = room;
|
||||||
|
@ -1047,20 +988,86 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderOmnichannelSection = () => {
|
||||||
|
const { room } = this.state;
|
||||||
|
const { rid, t } = room;
|
||||||
|
const { theme } = this.props;
|
||||||
|
|
||||||
|
if (t !== 'l' || this.isOmnichannelPreview) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Section>
|
||||||
|
{this.omnichannelPermissions?.canForwardGuest ? (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Forward'
|
||||||
|
onPress={() =>
|
||||||
|
this.onPressTouchable({
|
||||||
|
route: 'ForwardLivechatView',
|
||||||
|
params: { rid }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
left={() => <List.Icon name='chat-forward' color={themes[theme].titleText} />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{this.omnichannelPermissions?.canPlaceLivechatOnHold ? (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Place_chat_on_hold'
|
||||||
|
onPress={() =>
|
||||||
|
this.onPressTouchable({
|
||||||
|
event: this.placeOnHoldLivechat
|
||||||
|
})
|
||||||
|
}
|
||||||
|
left={() => <List.Icon name='pause' color={themes[theme].titleText} />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{this.omnichannelPermissions?.canReturnQueue ? (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Return_to_waiting_line'
|
||||||
|
onPress={() =>
|
||||||
|
this.onPressTouchable({
|
||||||
|
event: this.returnLivechat
|
||||||
|
})
|
||||||
|
}
|
||||||
|
left={() => <List.Icon name='move-to-the-queue' color={themes[theme].titleText} />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Close'
|
||||||
|
color={themes[theme].dangerColor}
|
||||||
|
onPress={() =>
|
||||||
|
this.onPressTouchable({
|
||||||
|
event: this.closeLivechat
|
||||||
|
})
|
||||||
|
}
|
||||||
|
left={() => <List.Icon name='chat-close' color={themes[theme].dangerColor} />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
</List.Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate } = this.state;
|
||||||
room,
|
|
||||||
membersCount,
|
|
||||||
canViewMembers,
|
|
||||||
canAddUser,
|
|
||||||
canInviteUser,
|
|
||||||
joined,
|
|
||||||
canAutoTranslate,
|
|
||||||
canForwardGuest,
|
|
||||||
canReturnQueue,
|
|
||||||
canViewCannedResponse,
|
|
||||||
canPlaceLivechatOnHold
|
|
||||||
} = this.state;
|
|
||||||
const { rid, t, prid } = room;
|
const { rid, t, prid } = room;
|
||||||
const isGroupChatHandler = isGroupChat(room);
|
const isGroupChatHandler = isGroupChat(room);
|
||||||
|
|
||||||
|
@ -1149,6 +1156,18 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{['l'].includes(t) && !this.isOmnichannelPreview && this.omnichannelPermissions?.canViewCannedResponse ? (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Canned_Responses'
|
||||||
|
onPress={() => this.onPressTouchable({ route: 'CannedResponsesListView', params: { rid } })}
|
||||||
|
left={() => <List.Icon name='canned-response' />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{['c', 'p', 'd'].includes(t) ? (
|
{['c', 'p', 'd'].includes(t) ? (
|
||||||
<>
|
<>
|
||||||
<List.Item
|
<List.Item
|
||||||
|
@ -1276,85 +1295,8 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
|
|
||||||
{this.teamChannelActions(t, room)}
|
{this.teamChannelActions(t, room)}
|
||||||
{this.teamToChannelActions(t, room)}
|
{this.teamToChannelActions(t, room)}
|
||||||
|
|
||||||
{['l'].includes(t) && !this.isOmnichannelPreview && canViewCannedResponse ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Canned_Responses'
|
|
||||||
onPress={() => this.onPressTouchable({ route: 'CannedResponsesListView', params: { rid } })}
|
|
||||||
left={() => <List.Icon name='canned-response' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['l'].includes(t) && !this.isOmnichannelPreview ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Close'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
event: this.closeLivechat
|
|
||||||
})
|
|
||||||
}
|
|
||||||
left={() => <List.Icon name='close' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['l'].includes(t) && !this.isOmnichannelPreview && canForwardGuest ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Forward'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
route: 'ForwardLivechatView',
|
|
||||||
params: { rid }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
left={() => <List.Icon name='user-forward' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['l'].includes(t) && !this.isOmnichannelPreview && canPlaceLivechatOnHold ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Place_chat_on_hold'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
event: this.placeOnHoldLivechat
|
|
||||||
})
|
|
||||||
}
|
|
||||||
left={() => <List.Icon name='pause' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['l'].includes(t) && !this.isOmnichannelPreview && canReturnQueue ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Return'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
event: this.returnLivechat
|
|
||||||
})
|
|
||||||
}
|
|
||||||
left={() => <List.Icon name='undo' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</List.Section>
|
</List.Section>
|
||||||
|
{this.renderOmnichannelSection()}
|
||||||
{this.renderLastSection()}
|
{this.renderLastSection()}
|
||||||
</List.Container>
|
</List.Container>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
@ -1377,12 +1319,9 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
editRoomPermission: state.permissions['edit-room'],
|
editRoomPermission: state.permissions['edit-room'],
|
||||||
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
||||||
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
||||||
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest'],
|
|
||||||
createTeamPermission: state.permissions['create-team'],
|
createTeamPermission: state.permissions['create-team'],
|
||||||
addTeamChannelPermission: state.permissions['add-team-channel'],
|
addTeamChannelPermission: state.permissions['add-team-channel'],
|
||||||
convertTeamPermission: state.permissions['convert-team'],
|
convertTeamPermission: state.permissions['convert-team']
|
||||||
viewCannedResponsesPermission: state.permissions['view-canned-responses'],
|
|
||||||
livechatAllowManualOnHold: state.settings.Livechat_allow_manual_on_hold as boolean
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(withDimensions(RoomActionsView)));
|
export default connect(mapStateToProps)(withTheme(withDimensions(RoomActionsView)));
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
|
@ -11,19 +12,33 @@ import { events, logEvent } from '../../lib/methods/helpers/log';
|
||||||
import { isTeamRoom } from '../../lib/methods/helpers/room';
|
import { isTeamRoom } from '../../lib/methods/helpers/room';
|
||||||
import { IApplicationState, SubscriptionType, TMessageModel, TSubscriptionModel } from '../../definitions';
|
import { IApplicationState, SubscriptionType, TMessageModel, TSubscriptionModel } from '../../definitions';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
|
import { TActionSheetOptions, TActionSheetOptionsItem, withActionSheet } from '../../containers/ActionSheet';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
|
||||||
|
import { closeRoom } from '../../actions/room';
|
||||||
|
import { onHoldLivechat, returnLivechat } from '../../lib/services/restApi';
|
||||||
|
|
||||||
interface IRightButtonsProps {
|
interface IRightButtonsProps {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
threadsEnabled: boolean;
|
threadsEnabled: boolean;
|
||||||
rid?: string;
|
rid: string;
|
||||||
t: string;
|
t: string;
|
||||||
tmid?: string;
|
tmid?: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
toggleFollowThread: Function;
|
toggleFollowThread: Function;
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
|
status?: string;
|
||||||
|
dispatch: Dispatch;
|
||||||
encrypted?: boolean;
|
encrypted?: boolean;
|
||||||
|
showActionSheet: (item: TActionSheetOptions) => void;
|
||||||
|
transferLivechatGuestPermission: boolean;
|
||||||
navigation: StackNavigationProp<ChatsStackParamList, 'RoomView'>;
|
navigation: StackNavigationProp<ChatsStackParamList, 'RoomView'>;
|
||||||
|
omnichannelPermissions: {
|
||||||
|
canForwardGuest: boolean;
|
||||||
|
canReturnQueue: boolean;
|
||||||
|
canPlaceLivechatOnHold: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRigthButtonsState {
|
interface IRigthButtonsState {
|
||||||
|
@ -71,13 +86,22 @@ class RightButtonsContainer extends Component<IRightButtonsProps, IRigthButtonsS
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: IRightButtonsProps, nextState: IRigthButtonsState) {
|
shouldComponentUpdate(nextProps: IRightButtonsProps, nextState: IRigthButtonsState) {
|
||||||
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
||||||
const { teamId } = this.props;
|
const { teamId, status, joined, omnichannelPermissions } = this.props;
|
||||||
if (nextProps.teamId !== teamId) {
|
if (nextProps.teamId !== teamId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextProps.status !== status) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (nextProps.joined !== joined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (nextState.isFollowingThread !== isFollowingThread) {
|
if (nextState.isFollowingThread !== isFollowingThread) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dequal(nextProps.omnichannelPermissions, omnichannelPermissions)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!dequal(nextState.tunread, tunread)) {
|
if (!dequal(nextState.tunread, tunread)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -157,6 +181,82 @@ class RightButtonsContainer extends Component<IRightButtonsProps, IRigthButtonsS
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
returnLivechat = () => {
|
||||||
|
const { rid } = this.props;
|
||||||
|
showConfirmationAlert({
|
||||||
|
message: i18n.t('Would_you_like_to_return_the_inquiry'),
|
||||||
|
confirmationText: i18n.t('Yes'),
|
||||||
|
onPress: async () => {
|
||||||
|
try {
|
||||||
|
await returnLivechat(rid);
|
||||||
|
} catch (e: any) {
|
||||||
|
showErrorAlert(e.reason, i18n.t('Oops'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
placeOnHoldLivechat = () => {
|
||||||
|
const { navigation, rid } = this.props;
|
||||||
|
showConfirmationAlert({
|
||||||
|
title: i18n.t('Are_you_sure_question_mark'),
|
||||||
|
message: i18n.t('Would_like_to_place_on_hold'),
|
||||||
|
confirmationText: i18n.t('Yes'),
|
||||||
|
onPress: async () => {
|
||||||
|
try {
|
||||||
|
await onHoldLivechat(rid);
|
||||||
|
navigation.navigate('RoomsListView');
|
||||||
|
} catch (e: any) {
|
||||||
|
showErrorAlert(e.data?.error, i18n.t('Oops'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
closeLivechat = () => {
|
||||||
|
const { dispatch, rid } = this.props;
|
||||||
|
dispatch(closeRoom(rid));
|
||||||
|
};
|
||||||
|
|
||||||
|
showMoreActions = () => {
|
||||||
|
logEvent(events.ROOM_SHOW_MORE_ACTIONS);
|
||||||
|
const { showActionSheet, rid, navigation, omnichannelPermissions } = this.props;
|
||||||
|
|
||||||
|
const options = [] as TActionSheetOptionsItem[];
|
||||||
|
if (omnichannelPermissions.canPlaceLivechatOnHold) {
|
||||||
|
options.push({
|
||||||
|
title: i18n.t('Place_chat_on_hold'),
|
||||||
|
icon: 'pause',
|
||||||
|
onPress: () => this.placeOnHoldLivechat()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (omnichannelPermissions.canForwardGuest) {
|
||||||
|
options.push({
|
||||||
|
title: i18n.t('Forward_Chat'),
|
||||||
|
icon: 'chat-forward',
|
||||||
|
onPress: () => navigation.navigate('ForwardLivechatView', { rid })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (omnichannelPermissions.canReturnQueue) {
|
||||||
|
options.push({
|
||||||
|
title: i18n.t('Return_to_waiting_line'),
|
||||||
|
icon: 'move-to-the-queue',
|
||||||
|
onPress: () => this.returnLivechat()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push({
|
||||||
|
title: i18n.t('Close'),
|
||||||
|
icon: 'chat-close',
|
||||||
|
onPress: () => this.closeLivechat(),
|
||||||
|
danger: true
|
||||||
|
});
|
||||||
|
|
||||||
|
showActionSheet({ options });
|
||||||
|
};
|
||||||
|
|
||||||
goSearchView = () => {
|
goSearchView = () => {
|
||||||
logEvent(events.ROOM_GO_SEARCH);
|
logEvent(events.ROOM_GO_SEARCH);
|
||||||
const { rid, t, navigation, isMasterDetail, encrypted } = this.props;
|
const { rid, t, navigation, isMasterDetail, encrypted } = this.props;
|
||||||
|
@ -183,10 +283,23 @@ class RightButtonsContainer extends Component<IRightButtonsProps, IRigthButtonsS
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isOmnichannelPreview = () => {
|
||||||
|
const { status } = this.props;
|
||||||
|
return status === 'queued';
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
||||||
const { t, tmid, threadsEnabled, teamId, joined } = this.props;
|
const { t, tmid, threadsEnabled, teamId, joined } = this.props;
|
||||||
|
|
||||||
if (t === 'l') {
|
if (t === 'l') {
|
||||||
|
if (!this.isOmnichannelPreview()) {
|
||||||
|
return (
|
||||||
|
<HeaderButton.Container>
|
||||||
|
<HeaderButton.Item iconName='kebab' onPress={this.showMoreActions} testID='room-view-header-omnichannel-kebab' />
|
||||||
|
</HeaderButton.Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (tmid) {
|
if (tmid) {
|
||||||
|
@ -225,4 +338,4 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(RightButtonsContainer);
|
export default connect(mapStateToProps)(withActionSheet(RightButtonsContainer));
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { dequal } from 'dequal';
|
||||||
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { getRoutingConfig } from '../../lib/services/restApi';
|
||||||
import Touch from '../../lib/methods/helpers/touch';
|
import Touch from '../../lib/methods/helpers/touch';
|
||||||
import { replyBroadcast } from '../../actions/messages';
|
import { replyBroadcast } from '../../actions/messages';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
|
@ -63,6 +64,7 @@ import {
|
||||||
IApplicationState,
|
IApplicationState,
|
||||||
IAttachment,
|
IAttachment,
|
||||||
IBaseScreen,
|
IBaseScreen,
|
||||||
|
ILastMessage,
|
||||||
ILoggedUser,
|
ILoggedUser,
|
||||||
IMessage,
|
IMessage,
|
||||||
IOmnichannelSource,
|
IOmnichannelSource,
|
||||||
|
@ -94,7 +96,8 @@ import {
|
||||||
canAutoTranslate as canAutoTranslateMethod,
|
canAutoTranslate as canAutoTranslateMethod,
|
||||||
debounce,
|
debounce,
|
||||||
isIOS,
|
isIOS,
|
||||||
isTablet
|
isTablet,
|
||||||
|
hasPermission
|
||||||
} from '../../lib/methods/helpers';
|
} from '../../lib/methods/helpers';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
|
||||||
|
@ -112,7 +115,10 @@ const stateAttrsUpdate = [
|
||||||
'reacting',
|
'reacting',
|
||||||
'readOnly',
|
'readOnly',
|
||||||
'member',
|
'member',
|
||||||
'showingBlockingLoader'
|
'showingBlockingLoader',
|
||||||
|
'canForwardGuest',
|
||||||
|
'canReturnQueue',
|
||||||
|
'canViewCannedResponse'
|
||||||
] as TStateAttrsUpdate[];
|
] as TStateAttrsUpdate[];
|
||||||
|
|
||||||
type TRoomUpdate = keyof TSubscriptionModel;
|
type TRoomUpdate = keyof TSubscriptionModel;
|
||||||
|
@ -138,6 +144,8 @@ const roomAttrsUpdate = [
|
||||||
'joinCodeRequired',
|
'joinCodeRequired',
|
||||||
'teamMain',
|
'teamMain',
|
||||||
'teamId',
|
'teamId',
|
||||||
|
'status',
|
||||||
|
'lastMessage',
|
||||||
'onHold'
|
'onHold'
|
||||||
] as TRoomUpdate[];
|
] as TRoomUpdate[];
|
||||||
|
|
||||||
|
@ -158,6 +166,9 @@ interface IRoomViewProps extends IBaseScreen<ChatsStackParamList, 'RoomView'> {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
insets: EdgeInsets;
|
insets: EdgeInsets;
|
||||||
|
transferLivechatGuestPermission?: string[]; // TODO: Check if its the correct type
|
||||||
|
viewCannedResponsesPermission?: string[]; // TODO: Check if its the correct type
|
||||||
|
livechatAllowManualOnHold?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRoomViewState {
|
interface IRoomViewState {
|
||||||
|
@ -165,7 +176,18 @@ interface IRoomViewState {
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
room:
|
room:
|
||||||
| TSubscriptionModel
|
| TSubscriptionModel
|
||||||
| { rid: string; t: string; name?: string; fname?: string; prid?: string; joinCodeRequired?: boolean; sysMes?: boolean };
|
| {
|
||||||
|
rid: string;
|
||||||
|
t: string;
|
||||||
|
name?: string;
|
||||||
|
fname?: string;
|
||||||
|
prid?: string;
|
||||||
|
joinCodeRequired?: boolean;
|
||||||
|
status?: boolean;
|
||||||
|
lastMessage?: ILastMessage;
|
||||||
|
sysMes?: boolean;
|
||||||
|
onHold?: boolean;
|
||||||
|
};
|
||||||
roomUpdate: {
|
roomUpdate: {
|
||||||
[K in TRoomUpdate]?: any;
|
[K in TRoomUpdate]?: any;
|
||||||
};
|
};
|
||||||
|
@ -197,6 +219,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
private flatList: TListRef;
|
private flatList: TListRef;
|
||||||
private mounted: boolean;
|
private mounted: boolean;
|
||||||
private offset = 0;
|
private offset = 0;
|
||||||
|
private subObserveQuery?: Subscription;
|
||||||
private subSubscription?: Subscription;
|
private subSubscription?: Subscription;
|
||||||
private queryUnreads?: Subscription;
|
private queryUnreads?: Subscription;
|
||||||
private retryInit = 0;
|
private retryInit = 0;
|
||||||
|
@ -256,8 +279,14 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
reacting: false,
|
reacting: false,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
unreadsCount: null,
|
unreadsCount: null,
|
||||||
roomUserId
|
roomUserId,
|
||||||
|
canViewCannedResponse: false,
|
||||||
|
canForwardGuest: false,
|
||||||
|
canReturnQueue: false,
|
||||||
|
canPlaceLivechatOnHold: false,
|
||||||
|
isOnHold: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setHeader();
|
this.setHeader();
|
||||||
|
|
||||||
if ('id' in room) {
|
if ('id' in room) {
|
||||||
|
@ -275,6 +304,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
this.flatList = React.createRef();
|
this.flatList = React.createRef();
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
|
|
||||||
|
if (this.t === 'l') {
|
||||||
|
this.updateOmnichannel();
|
||||||
|
}
|
||||||
|
|
||||||
// we don't need to subscribe to threads
|
// we don't need to subscribe to threads
|
||||||
if (this.rid && !this.tmid) {
|
if (this.rid && !this.tmid) {
|
||||||
this.sub = new RoomClass(this.rid);
|
this.sub = new RoomClass(this.rid);
|
||||||
|
@ -314,7 +347,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: IRoomViewProps, nextState: IRoomViewState) {
|
shouldComponentUpdate(nextProps: IRoomViewProps, nextState: IRoomViewState) {
|
||||||
const { state } = this;
|
const { state } = this;
|
||||||
const { roomUpdate, member } = state;
|
const { roomUpdate, member, isOnHold } = state;
|
||||||
const { appState, theme, insets, route } = this.props;
|
const { appState, theme, insets, route } = this.props;
|
||||||
if (theme !== nextProps.theme) {
|
if (theme !== nextProps.theme) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -325,7 +358,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
if (member.statusText !== nextState.member.statusText) {
|
if (member.statusText !== nextState.member.statusText) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (isOnHold !== nextState.isOnHold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const stateUpdated = stateAttrsUpdate.some(key => nextState[key] !== state[key]);
|
const stateUpdated = stateAttrsUpdate.some(key => nextState[key] !== state[key]);
|
||||||
if (stateUpdated) {
|
if (stateUpdated) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -340,7 +375,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IRoomViewProps, prevState: IRoomViewState) {
|
componentDidUpdate(prevProps: IRoomViewProps, prevState: IRoomViewState) {
|
||||||
const { roomUpdate } = this.state;
|
const { roomUpdate, joined } = this.state;
|
||||||
const { appState, insets, route } = this.props;
|
const { appState, insets, route } = this.props;
|
||||||
|
|
||||||
if (route?.params?.jumpToMessageId && route?.params?.jumpToMessageId !== prevProps.route?.params?.jumpToMessageId) {
|
if (route?.params?.jumpToMessageId && route?.params?.jumpToMessageId !== prevProps.route?.params?.jumpToMessageId) {
|
||||||
|
@ -365,8 +400,13 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
}
|
}
|
||||||
// If it's a livechat room
|
// If it's a livechat room
|
||||||
if (this.t === 'l') {
|
if (this.t === 'l') {
|
||||||
if (!dequal(prevState.roomUpdate.visitor, roomUpdate.visitor)) {
|
if (
|
||||||
this.setHeader();
|
!dequal(prevState.roomUpdate.lastMessage?.token, roomUpdate.lastMessage?.token) ||
|
||||||
|
!dequal(prevState.roomUpdate.visitor, roomUpdate.visitor) ||
|
||||||
|
!dequal(prevState.roomUpdate.status, roomUpdate.status) ||
|
||||||
|
prevState.joined !== joined
|
||||||
|
) {
|
||||||
|
this.updateOmnichannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (roomUpdate.teamMain !== prevState.roomUpdate.teamMain || roomUpdate.teamId !== prevState.roomUpdate.teamId) {
|
if (roomUpdate.teamMain !== prevState.roomUpdate.teamMain || roomUpdate.teamId !== prevState.roomUpdate.teamId) {
|
||||||
|
@ -387,6 +427,17 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
this.setReadOnly();
|
this.setReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateOmnichannel = async () => {
|
||||||
|
const canForwardGuest = await this.canForwardGuest();
|
||||||
|
const canPlaceLivechatOnHold = this.canPlaceLivechatOnHold();
|
||||||
|
const canReturnQueue = await this.canReturnQueue();
|
||||||
|
const canViewCannedResponse = await this.canViewCannedResponse();
|
||||||
|
this.setState({ canForwardGuest, canReturnQueue, canViewCannedResponse, canPlaceLivechatOnHold });
|
||||||
|
if (this.mounted) {
|
||||||
|
this.setHeader();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async componentWillUnmount() {
|
async componentWillUnmount() {
|
||||||
const { editing, room } = this.state;
|
const { editing, room } = this.state;
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
@ -424,15 +475,16 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
if (this.subSubscription && this.subSubscription.unsubscribe) {
|
if (this.subSubscription && this.subSubscription.unsubscribe) {
|
||||||
this.subSubscription.unsubscribe();
|
this.subSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.subObserveQuery && this.subObserveQuery.unsubscribe) {
|
||||||
|
this.subObserveQuery.unsubscribe();
|
||||||
|
}
|
||||||
if (this.queryUnreads && this.queryUnreads.unsubscribe) {
|
if (this.queryUnreads && this.queryUnreads.unsubscribe) {
|
||||||
this.queryUnreads.unsubscribe();
|
this.queryUnreads.unsubscribe();
|
||||||
}
|
}
|
||||||
if (this.retryInitTimeout) {
|
if (this.retryInitTimeout) {
|
||||||
clearTimeout(this.retryInitTimeout);
|
clearTimeout(this.retryInitTimeout);
|
||||||
}
|
}
|
||||||
if (this.retryFindTimeout) {
|
|
||||||
clearTimeout(this.retryFindTimeout);
|
|
||||||
}
|
|
||||||
EventEmitter.removeListener('connected', this.handleConnected);
|
EventEmitter.removeListener('connected', this.handleConnected);
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
EventEmitter.removeListener(KEY_COMMAND, this.handleCommands);
|
EventEmitter.removeListener(KEY_COMMAND, this.handleCommands);
|
||||||
|
@ -441,13 +493,61 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
console.countReset(`${this.constructor.name}.render calls`);
|
console.countReset(`${this.constructor.name}.render calls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canForwardGuest = async () => {
|
||||||
|
const { transferLivechatGuestPermission } = this.props;
|
||||||
|
const permissions = await hasPermission([transferLivechatGuestPermission], this.rid);
|
||||||
|
return permissions[0] as boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
canPlaceLivechatOnHold = () => {
|
||||||
|
const { livechatAllowManualOnHold } = this.props;
|
||||||
|
const { room } = this.state;
|
||||||
|
return !!(livechatAllowManualOnHold && !room?.lastMessage?.token && room?.lastMessage?.u && !room.onHold);
|
||||||
|
};
|
||||||
|
|
||||||
|
canViewCannedResponse = async () => {
|
||||||
|
const { viewCannedResponsesPermission } = this.props;
|
||||||
|
const permissions = await hasPermission([viewCannedResponsesPermission], this.rid);
|
||||||
|
return permissions[0] as boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
canReturnQueue = async () => {
|
||||||
|
try {
|
||||||
|
const { returnQueue } = await getRoutingConfig();
|
||||||
|
return returnQueue;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
observeSubscriptions = () => {
|
||||||
|
try {
|
||||||
|
const db = database.active;
|
||||||
|
const observeSubCollection = db
|
||||||
|
.get('subscriptions')
|
||||||
|
.query(Q.where('rid', this.rid as string))
|
||||||
|
.observe();
|
||||||
|
this.subObserveQuery = observeSubCollection.subscribe(data => {
|
||||||
|
if (data[0]) {
|
||||||
|
if (this.subObserveQuery && this.subObserveQuery.unsubscribe) {
|
||||||
|
this.observeRoom(data[0]);
|
||||||
|
this.setState({ room: data[0] });
|
||||||
|
this.subObserveQuery.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("observeSubscriptions: Can't find subscription to observe");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
get isOmnichannel() {
|
get isOmnichannel() {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
return room.t === 'l';
|
return room.t === 'l';
|
||||||
}
|
}
|
||||||
|
|
||||||
setHeader = () => {
|
setHeader = () => {
|
||||||
const { room, unreadsCount, roomUserId, joined } = this.state;
|
const { room, unreadsCount, roomUserId, joined, canForwardGuest, canReturnQueue, canPlaceLivechatOnHold } = this.state;
|
||||||
const { navigation, isMasterDetail, theme, baseUrl, user, route } = this.props;
|
const { navigation, isMasterDetail, theme, baseUrl, user, route } = this.props;
|
||||||
const { rid, tmid } = this;
|
const { rid, tmid } = this;
|
||||||
if (!room.rid) {
|
if (!room.rid) {
|
||||||
|
@ -474,6 +574,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
let token: string | undefined;
|
let token: string | undefined;
|
||||||
let avatar: string | undefined;
|
let avatar: string | undefined;
|
||||||
let visitor: IVisitor | undefined;
|
let visitor: IVisitor | undefined;
|
||||||
|
let status: string | undefined;
|
||||||
let sourceType: IOmnichannelSource | undefined;
|
let sourceType: IOmnichannelSource | undefined;
|
||||||
if ('id' in room) {
|
if ('id' in room) {
|
||||||
subtitle = room.topic;
|
subtitle = room.topic;
|
||||||
|
@ -484,6 +585,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;
|
||||||
|
status = room.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('source' in room) {
|
if ('source' in room) {
|
||||||
|
@ -493,11 +595,13 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let numIconsRight = 2;
|
let numIconsRight = 2;
|
||||||
if (tmid) {
|
if (tmid || (status && joined)) {
|
||||||
numIconsRight = 1;
|
numIconsRight = 1;
|
||||||
} else if (teamId && isTeamRoom({ teamId, joined })) {
|
} else if (teamId && isTeamRoom({ teamId, joined })) {
|
||||||
numIconsRight = 3;
|
numIconsRight = 3;
|
||||||
}
|
}
|
||||||
|
const omnichannelPermissions = { canForwardGuest, canReturnQueue, canPlaceLivechatOnHold };
|
||||||
|
|
||||||
const paddingRight = this.getPaddingLeft(numIconsRight, isMasterDetail);
|
const paddingRight = this.getPaddingLeft(numIconsRight, isMasterDetail);
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
|
@ -542,6 +646,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
tmid={tmid}
|
tmid={tmid}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
joined={joined}
|
joined={joined}
|
||||||
|
status={room.status}
|
||||||
|
omnichannelPermissions={omnichannelPermissions}
|
||||||
t={this.t || t}
|
t={this.t || t}
|
||||||
encrypted={encrypted}
|
encrypted={encrypted}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
@ -560,7 +666,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
|
|
||||||
goRoomActionsView = (screen?: keyof ModalStackParamList) => {
|
goRoomActionsView = (screen?: keyof ModalStackParamList) => {
|
||||||
logEvent(events.ROOM_GO_RA);
|
logEvent(events.ROOM_GO_RA);
|
||||||
const { room, member, joined } = this.state;
|
const { room, member, joined, canForwardGuest, canReturnQueue, canViewCannedResponse, canPlaceLivechatOnHold } = this.state;
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -572,7 +678,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
room: room as ISubscription,
|
room: room as ISubscription,
|
||||||
member,
|
member,
|
||||||
showCloseModal: !!screen,
|
showCloseModal: !!screen,
|
||||||
joined
|
joined,
|
||||||
|
omnichannelPermissions: { canForwardGuest, canReturnQueue, canViewCannedResponse, canPlaceLivechatOnHold }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (this.rid && this.t) {
|
} else if (this.rid && this.t) {
|
||||||
|
@ -581,7 +688,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
t: this.t as SubscriptionType,
|
t: this.t as SubscriptionType,
|
||||||
room: room as TSubscriptionModel,
|
room: room as TSubscriptionModel,
|
||||||
member,
|
member,
|
||||||
joined
|
joined,
|
||||||
|
omnichannelPermissions: { canForwardGuest, canReturnQueue, canViewCannedResponse, canPlaceLivechatOnHold }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -669,15 +777,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
this.internalSetState({ joined: false });
|
this.internalSetState({ joined: false });
|
||||||
}
|
}
|
||||||
if (this.rid) {
|
if (this.rid) {
|
||||||
// We navigate to RoomView before the Room is inserted to the local db
|
this.observeSubscriptions();
|
||||||
// So we retry just to make sure we have the right content
|
|
||||||
this.retryFindCount = this.retryFindCount + 1 || 1;
|
|
||||||
if (this.retryFindCount <= 3) {
|
|
||||||
this.retryFindTimeout = setTimeout(() => {
|
|
||||||
this.findAndObserveRoom(rid);
|
|
||||||
this.init();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -697,7 +797,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
return ret;
|
return ret;
|
||||||
}, {});
|
}, {});
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.internalSetState({ room: changes, roomUpdate });
|
this.internalSetState({ room: changes, roomUpdate, isOnHold: !!changes?.onHold });
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.state.room = changes;
|
this.state.room = changes;
|
||||||
|
@ -1186,6 +1286,11 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
goToCannedResponses = () => {
|
||||||
|
const { room } = this.state;
|
||||||
|
Navigation.navigate('CannedResponsesListView', { rid: room.rid });
|
||||||
|
};
|
||||||
|
|
||||||
renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => {
|
renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => {
|
||||||
const { room, lastOpen, canAutoTranslate } = this.state;
|
const { room, lastOpen, canAutoTranslate } = this.state;
|
||||||
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
|
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
|
||||||
|
@ -1203,7 +1308,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
dateSeparator = item.ts;
|
dateSeparator = item.ts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = null;
|
let content = null;
|
||||||
if (item.t && MESSAGE_TYPE_ANY_LOAD.includes(item.t as MessageTypeLoad)) {
|
if (item.t && MESSAGE_TYPE_ANY_LOAD.includes(item.t as MessageTypeLoad)) {
|
||||||
content = (
|
content = (
|
||||||
|
@ -1271,7 +1375,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderFooter = () => {
|
renderFooter = () => {
|
||||||
const { joined, room, selectedMessage, editing, replying, replyWithMention, readOnly, loading } = this.state;
|
const { joined, room, selectedMessage, editing, replying, replyWithMention, readOnly, loading, canViewCannedResponse } =
|
||||||
|
this.state;
|
||||||
const { navigation, theme, route } = this.props;
|
const { navigation, theme, route } = this.props;
|
||||||
|
|
||||||
const usedCannedResponse = route?.params?.usedCannedResponse;
|
const usedCannedResponse = route?.params?.usedCannedResponse;
|
||||||
|
@ -1338,9 +1443,11 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
return (
|
return (
|
||||||
<MessageBox
|
<MessageBox
|
||||||
ref={this.messagebox}
|
ref={this.messagebox}
|
||||||
|
goToCannedResponses={canViewCannedResponse ? this.goToCannedResponses : null}
|
||||||
onSubmit={this.handleSendMessage}
|
onSubmit={this.handleSendMessage}
|
||||||
rid={this.rid}
|
rid={this.rid}
|
||||||
tmid={this.tmid}
|
tmid={this.tmid}
|
||||||
|
joined={joined}
|
||||||
roomType={room.t}
|
roomType={room.t}
|
||||||
isFocused={navigation.isFocused}
|
isFocused={navigation.isFocused}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -1454,7 +1561,10 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
serverVersion: state.server.version,
|
serverVersion: state.server.version,
|
||||||
Message_Read_Receipt_Enabled: state.settings.Message_Read_Receipt_Enabled as boolean,
|
Message_Read_Receipt_Enabled: state.settings.Message_Read_Receipt_Enabled as boolean,
|
||||||
Hide_System_Messages: state.settings.Hide_System_Messages as string[]
|
Hide_System_Messages: state.settings.Hide_System_Messages as string[],
|
||||||
|
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest'],
|
||||||
|
viewCannedResponsesPermission: state.permissions['view-canned-responses'],
|
||||||
|
livechatAllowManualOnHold: state.settings.Livechat_allow_manual_on_hold as boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomView))));
|
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomView))));
|
||||||
|
|
Loading…
Reference in New Issue