From 8012031cd37316e6c6f7df81b2dd1968ccc5b1d1 Mon Sep 17 00:00:00 2001
From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>
Date: Wed, 20 Apr 2022 17:53:11 -0300
Subject: [PATCH] [NEW] On-Hold chats for Omnichannel (#4051)
* [NEW] Implementing On-Hold Livechat for Omnichannel
* added onHold to database
* list header title open livechats
* update rooms list view
* remove placeOnHold after clicked
* fix mesasgebox reactive to on hold
* navigate to roomslistview
* minor tweaks
* for grouping too
* fix chat on-hold when the agent is fully
* show on hold system messages
---
app/containers/message/index.tsx | 4 +-
app/containers/message/interfaces.ts | 1 +
app/containers/message/utils.ts | 17 ++++--
app/definitions/IMessage.ts | 2 +
app/definitions/IRoom.ts | 2 +
app/definitions/ISubscription.ts | 1 +
app/ee/omnichannel/lib/index.ts | 3 ++
app/i18n/locales/ar.json | 1 -
app/i18n/locales/de.json | 1 -
app/i18n/locales/en.json | 9 +++-
app/i18n/locales/fr.json | 1 -
app/i18n/locales/it.json | 1 -
app/i18n/locales/nl.json | 1 -
app/i18n/locales/pt-BR.json | 1 -
app/i18n/locales/pt-PT.json | 1 -
app/i18n/locales/ru.json | 1 -
app/i18n/locales/tr.json | 1 -
app/i18n/locales/zh-CN.json | 1 -
app/i18n/locales/zh-TW.json | 1 -
app/lib/constants/defaultSettings.ts | 3 ++
app/lib/database/model/Message.js | 2 +
app/lib/database/model/Subscription.js | 2 +
app/lib/database/model/migrations.js | 13 +++++
app/lib/database/schema/app.js | 6 ++-
app/lib/methods/subscriptions/rooms.ts | 11 +++-
app/lib/services/restApi.ts | 2 +
app/utils/log/events.ts | 1 +
app/views/RoomActionsView/index.tsx | 72 ++++++++++++++++++++++++--
app/views/RoomView/index.tsx | 39 +++++++++++++-
app/views/RoomsListView/index.tsx | 32 +++++++++---
30 files changed, 198 insertions(+), 35 deletions(-)
diff --git a/app/containers/message/index.tsx b/app/containers/message/index.tsx
index 599ffa3c9..4facaf9e5 100644
--- a/app/containers/message/index.tsx
+++ b/app/containers/message/index.tsx
@@ -354,7 +354,8 @@ class MessageContainer extends React.Component
);
diff --git a/app/containers/message/interfaces.ts b/app/containers/message/interfaces.ts
index f71560536..72047147d 100644
--- a/app/containers/message/interfaces.ts
+++ b/app/containers/message/interfaces.ts
@@ -59,6 +59,7 @@ export interface IMessageContent {
useRealName?: boolean;
isIgnored: boolean;
type: string;
+ comment?: string;
}
export interface IMessageEmoji {
diff --git a/app/containers/message/utils.ts b/app/containers/message/utils.ts
index ff1dc7365..6bac3d86b 100644
--- a/app/containers/message/utils.ts
+++ b/app/containers/message/utils.ts
@@ -55,7 +55,9 @@ export const SYSTEM_MESSAGES = [
'user-converted-to-team',
'user-converted-to-channel',
'user-deleted-room-from-team',
- 'user-removed-room-from-team'
+ 'user-removed-room-from-team',
+ 'omnichannel_placed_chat_on_hold',
+ 'omnichannel_on_hold_chat_resumed'
];
export const SYSTEM_MESSAGE_TYPES = {
@@ -73,7 +75,9 @@ export const SYSTEM_MESSAGE_TYPES = {
CONVERTED_TO_TEAM: 'user-converted-to-team',
CONVERTED_TO_CHANNEL: 'user-converted-to-channel',
DELETED_ROOM_FROM_TEAM: 'user-deleted-room-from-team',
- REMOVED_ROOM_FROM_TEAM: 'user-removed-room-from-team'
+ REMOVED_ROOM_FROM_TEAM: 'user-removed-room-from-team',
+ OMNICHANNEL_PLACED_CHAT_ON_HOLD: 'omnichannel_placed_chat_on_hold',
+ OMNICHANNEL_ON_HOLD_CHAT_RESUMED: 'omnichannel_on_hold_chat_resumed'
};
export const SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME = [
@@ -99,9 +103,10 @@ type TInfoMessage = {
role: string;
msg: string;
author: { username: string };
+ comment?: string;
};
-export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage): string => {
+export const getInfoMessage = ({ type, role, msg, author, comment }: TInfoMessage): string => {
const { username } = author;
if (type === 'rm') {
return I18n.t('Message_removed');
@@ -193,6 +198,12 @@ export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage): strin
if (type === 'user-removed-room-from-team') {
return I18n.t('Removed__roomName__from_this_team', { roomName: msg });
}
+ if (type === 'omnichannel_placed_chat_on_hold') {
+ return I18n.t('Omnichannel_placed_chat_on_hold', { comment });
+ }
+ if (type === 'omnichannel_on_hold_chat_resumed') {
+ return I18n.t('Omnichannel_on_hold_chat_resumed', { comment });
+ }
return '';
};
diff --git a/app/definitions/IMessage.ts b/app/definitions/IMessage.ts
index ff32100c0..f119ddfcd 100644
--- a/app/definitions/IMessage.ts
+++ b/app/definitions/IMessage.ts
@@ -60,6 +60,7 @@ export interface ILastMessage {
reactions?: IReaction[];
unread?: boolean;
status?: number;
+ token?: string;
}
interface IMessageFile {
@@ -140,6 +141,7 @@ export interface IMessage extends IMessageFromServer {
blocks?: any;
e2e?: E2EType;
tshow?: boolean;
+ comment?: string;
subscription?: { id: string };
}
diff --git a/app/definitions/IRoom.ts b/app/definitions/IRoom.ts
index 6c55d7a9c..ed6f3d2b0 100644
--- a/app/definitions/IRoom.ts
+++ b/app/definitions/IRoom.ts
@@ -55,6 +55,8 @@ export interface IRoom {
uids: Array;
lm?: Date;
sysMes?: string[];
+ onHold?: boolean;
+ waitingResponse?: boolean;
}
export enum OmnichannelSourceType {
diff --git a/app/definitions/ISubscription.ts b/app/definitions/ISubscription.ts
index a29e46f73..e2e6f2739 100644
--- a/app/definitions/ISubscription.ts
+++ b/app/definitions/ISubscription.ts
@@ -98,6 +98,7 @@ export interface ISubscription {
teamMain?: boolean;
unsubscribe: () => Promise;
separator?: boolean;
+ onHold?: boolean;
source?: IOmnichannelSource;
// https://nozbe.github.io/WatermelonDB/Relation.html#relation-api
messages: RelationModified;
diff --git a/app/ee/omnichannel/lib/index.ts b/app/ee/omnichannel/lib/index.ts
index efe6d5083..5ee8e1c59 100644
--- a/app/ee/omnichannel/lib/index.ts
+++ b/app/ee/omnichannel/lib/index.ts
@@ -17,6 +17,9 @@ export const getInquiriesQueued = () => sdk.get('livechat/inquiries.queued');
// RC 2.4.0
export const takeInquiry = (inquiryId: string) => sdk.methodCallWrapper('livechat:takeInquiry', inquiryId);
+// RC 4.26
+export const takeResume = (roomId: string) => sdk.methodCallWrapper('livechat:resumeOnHold', roomId);
+
class Omnichannel {
private inquirySub: { stop: () => void } | null;
constructor() {
diff --git a/app/i18n/locales/ar.json b/app/i18n/locales/ar.json
index c4084cd48..87fe5f894 100644
--- a/app/i18n/locales/ar.json
+++ b/app/i18n/locales/ar.json
@@ -358,7 +358,6 @@
"Offline": "غير متصل",
"Oops": "عفوًا!",
"Omnichannel": "القنوات الموحدة",
- "Open_Livechats": "محادثات مباشرة جارية",
"Omnichannel_enable_alert": "أنت غير متاح ",
"Onboarding_description": "مساحة عمل هي مساحة لفريقك ومنظمتك للتعاون. اطلب من المشرف العنوان للانضمام أو أنشئ لفريقك",
"Onboarding_join_workspace": "انضم لمساحة عمل",
diff --git a/app/i18n/locales/de.json b/app/i18n/locales/de.json
index 20558faef..61b8d57c4 100644
--- a/app/i18n/locales/de.json
+++ b/app/i18n/locales/de.json
@@ -363,7 +363,6 @@
"Offline": "Offline",
"Oops": "Hoppla!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Offene Chats",
"Omnichannel_enable_alert": "Sie sind in Omnichannel nicht verfügbar. Möchten Sie erreichbar sein?",
"Onboarding_description": "Ein Arbeitsbereich ist der Ort für die Zusammenarbeit deines Teams oder Organisation. Bitten Sie den Admin des Arbeitsbereichs um eine Adresse, um ihm beizutreten, oder erstellen Sie einen Arbeitsbereich für Ihr Team.",
"Onboarding_join_workspace": "Einem Arbeitsbereich beiteten",
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index ff3c340b2..c06a4ef00 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -363,7 +363,6 @@
"Offline": "Offline",
"Oops": "Oops!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Chats in Progress",
"Omnichannel_enable_alert": "You're not available on Omnichannel. Would you like to be available?",
"Onboarding_description": "A workspace is your team or organization’s space to collaborate. Ask the workspace admin for address to join or create one for your team.",
"Onboarding_join_workspace": "Join a workspace",
@@ -808,6 +807,14 @@
"Removed__username__from_team": "removed @{{user_removed}} from this Team",
"User_joined_team": "joined this Team",
"User_left_team": "left this Team",
+ "Place_chat_on_hold": "Place chat on-hold",
+ "Would_like_to_place_on_hold": "Would you like to place this chat On-Hold?",
+ "Open_Livechats": "Omnichannel chats in progress",
+ "On_hold_Livechats": "Omnichannel chats on hold",
+ "Chat_is_on_hold": "This chat is on-hold due to inactivity",
+ "Resume": "Resume",
+ "Omnichannel_placed_chat_on_hold": "Chat On Hold: {{comment}}",
+ "Omnichannel_on_hold_chat_resumed": "On Hold Chat Resumed: {{comment}}",
"Omnichannel_queue": "Omnichannel queue",
"Empty": "Empty"
}
\ No newline at end of file
diff --git a/app/i18n/locales/fr.json b/app/i18n/locales/fr.json
index 92f5e7fb7..57a2eab73 100644
--- a/app/i18n/locales/fr.json
+++ b/app/i18n/locales/fr.json
@@ -363,7 +363,6 @@
"Offline": "Hors ligne",
"Oops": "Oups !",
"Omnichannel": "Omnicanal",
- "Open_Livechats": "Discussions en cours",
"Omnichannel_enable_alert": "Vous n'êtes pas disponible sur Omnicanal. Souhaitez-vous être disponible ?",
"Onboarding_description": "Un espace de travail est l'espace de collaboration de votre équipe ou organisation. Demandez à l'administrateur de l'espace de travail l'adresse pour rejoindre ou créez-en une pour votre équipe.",
"Onboarding_join_workspace": "Rejoindre un espace de travail",
diff --git a/app/i18n/locales/it.json b/app/i18n/locales/it.json
index 664ce46fb..b825241ac 100644
--- a/app/i18n/locales/it.json
+++ b/app/i18n/locales/it.json
@@ -352,7 +352,6 @@
"Offline": "Offline",
"Oops": "Oops!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Chat in corso",
"Omnichannel_enable_alert": "Non sei ancora su Onmichannel. Vuoi attivarlo?",
"Onboarding_description": "Un workspace è lo spazio dove il tuo team o la tua organizzazione possono collaborare. Chiedi l'indirizzo all'amministratore del tuo workspace oppure creane uno per il tuo team.",
"Onboarding_join_workspace": "Unisciti",
diff --git a/app/i18n/locales/nl.json b/app/i18n/locales/nl.json
index 09ae146e0..ce0377bf8 100644
--- a/app/i18n/locales/nl.json
+++ b/app/i18n/locales/nl.json
@@ -363,7 +363,6 @@
"Offline": "Offline",
"Oops": "Oeps!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Bezig met chatten",
"Omnichannel_enable_alert": "Je bent niet beschikbaar op Omnichannel. Wil je beschikbaar zijn?",
"Onboarding_description": "Een werkruimte is de ruimte van jouw team of organisatie om samen te werken. Vraag aan de beheerder van de werkruimte een adres om lid te worden of maak er een aan voor jouw team.",
"Onboarding_join_workspace": "Word lid van een werkruimte",
diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json
index 109d8c8a8..1e1939b39 100644
--- a/app/i18n/locales/pt-BR.json
+++ b/app/i18n/locales/pt-BR.json
@@ -339,7 +339,6 @@
"Offline": "Offline",
"Oops": "Ops!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Bate-papos em Andamento",
"Omnichannel_enable_alert": "Você não está disponível no Omnichannel. Você quer ficar disponível?",
"Onboarding_description": "Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.",
"Onboarding_join_workspace": "Entre numa workspace",
diff --git a/app/i18n/locales/pt-PT.json b/app/i18n/locales/pt-PT.json
index f2cd45e7b..9509842ec 100644
--- a/app/i18n/locales/pt-PT.json
+++ b/app/i18n/locales/pt-PT.json
@@ -359,7 +359,6 @@
"Offline": "Desligado",
"Oops": "Oops!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Chats em andamento",
"Omnichannel_enable_alert": "Você não está disponível no Omnichannel. Você gostaria de estar disponível?",
"Onboarding_description": "Um espaço de trabalho é o espaço da sua equipa ou organização para colaborar. Peça ao administrador do espaço de trabalho um endereço para se juntar ou criar um para a sua equipa.",
"Onboarding_join_workspace": "Junte-se a um espaço de trabalho",
diff --git a/app/i18n/locales/ru.json b/app/i18n/locales/ru.json
index e07ce81ea..eb4b7037c 100644
--- a/app/i18n/locales/ru.json
+++ b/app/i18n/locales/ru.json
@@ -363,7 +363,6 @@
"Offline": "Офлайн",
"Oops": "Упс!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "Чаты в Работе",
"Omnichannel_enable_alert": "Вы не доступны в Omnichannel. Хотите стать доступными?",
"Onboarding_description": "Сервер это пространство для взаимодействия вашей команды или организации. Уточните адрес сервера у вашего администратора или создайте свой сервер для команды.",
"Onboarding_join_workspace": "Присоединиться к серверу",
diff --git a/app/i18n/locales/tr.json b/app/i18n/locales/tr.json
index 80eacc334..64d88304f 100644
--- a/app/i18n/locales/tr.json
+++ b/app/i18n/locales/tr.json
@@ -353,7 +353,6 @@
"Offline": "Çevrimdışı",
"Oops": "Ahh!",
"Omnichannel": "Çoklu Kanal",
- "Open_Livechats": "Devam Eden Sohbetler",
"Omnichannel_enable_alert": "Çoklu Kanal'da mevcut değilsiniz. Erişilebilir olmak ister misiniz?",
"Onboarding_description": "Çalışma alanı, ekibinizin veya kuruluşunuzun işbirliği alanıdır. Çalışma alanı yöneticisinden bir ekibe katılmak veya bir ekip oluşturmak için yardım isteyin.",
"Onboarding_join_workspace": "Bir çalışma alanına katılın",
diff --git a/app/i18n/locales/zh-CN.json b/app/i18n/locales/zh-CN.json
index 5a3b41526..c95e15330 100644
--- a/app/i18n/locales/zh-CN.json
+++ b/app/i18n/locales/zh-CN.json
@@ -350,7 +350,6 @@
"Offline": "离线",
"Oops": "哎呀!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "打开即时聊天",
"Omnichannel_enable_alert": "您尚未启用 Omnichannel,是否想要启用?",
"Onboarding_description": "工作区是团队或组织协作的空间。向工作区管理员询问要加入的地址或为您的团队创建一个。",
"Onboarding_join_workspace": "加入一个工作区",
diff --git a/app/i18n/locales/zh-TW.json b/app/i18n/locales/zh-TW.json
index e72bf0864..d153add8e 100644
--- a/app/i18n/locales/zh-TW.json
+++ b/app/i18n/locales/zh-TW.json
@@ -352,7 +352,6 @@
"Offline": "離線",
"Oops": "哎呀!",
"Omnichannel": "Omnichannel",
- "Open_Livechats": "打開即時聊天",
"Omnichannel_enable_alert": "您尚未啟用 Omnichannel,是否想要啟用?",
"Onboarding_description": "工作區是團隊或組織協作的空間。向工作區管理員詢問要加入的地址或為您的團隊創建一個。",
"Onboarding_join_workspace": "加入一個工作區",
diff --git a/app/lib/constants/defaultSettings.ts b/app/lib/constants/defaultSettings.ts
index b140e89ff..656cefbd8 100644
--- a/app/lib/constants/defaultSettings.ts
+++ b/app/lib/constants/defaultSettings.ts
@@ -206,6 +206,9 @@ export const defaultSettings = {
Canned_Responses_Enable: {
type: 'valueAsBoolean'
},
+ Livechat_allow_manual_on_hold: {
+ type: 'valueAsBoolean'
+ },
Accounts_AvatarExternalProviderUrl: {
type: 'valueAsString'
}
diff --git a/app/lib/database/model/Message.js b/app/lib/database/model/Message.js
index 392283a63..9592f4e60 100644
--- a/app/lib/database/model/Message.js
+++ b/app/lib/database/model/Message.js
@@ -83,4 +83,6 @@ export default class Message extends Model {
@field('tshow') tshow;
@json('md', sanitizer) md;
+
+ @field('comment') comment;
}
diff --git a/app/lib/database/model/Subscription.js b/app/lib/database/model/Subscription.js
index 76916c842..46f990562 100644
--- a/app/lib/database/model/Subscription.js
+++ b/app/lib/database/model/Subscription.js
@@ -131,5 +131,7 @@ export default class Subscription extends Model {
@field('team_main') teamMain;
+ @field('on_hold') onHold;
+
@json('source', sanitizer) source;
}
diff --git a/app/lib/database/model/migrations.js b/app/lib/database/model/migrations.js
index f0446048e..8aa813f4d 100644
--- a/app/lib/database/model/migrations.js
+++ b/app/lib/database/model/migrations.js
@@ -217,6 +217,19 @@ export default schemaMigrations({
columns: [{ name: 'source', type: 'string', isOptional: true }]
})
]
+ },
+ {
+ toVersion: 17,
+ steps: [
+ addColumns({
+ table: 'subscriptions',
+ columns: [{ name: 'on_hold', type: 'boolean', isOptional: true }]
+ }),
+ addColumns({
+ table: 'messages',
+ columns: [{ name: 'comment', type: 'string', isOptional: true }]
+ })
+ ]
}
]
});
diff --git a/app/lib/database/schema/app.js b/app/lib/database/schema/app.js
index d0f442cd3..dac45346c 100644
--- a/app/lib/database/schema/app.js
+++ b/app/lib/database/schema/app.js
@@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
- version: 16,
+ version: 17,
tables: [
tableSchema({
name: 'subscriptions',
@@ -60,6 +60,7 @@ export default appSchema({
{ name: 'avatar_etag', type: 'string', isOptional: true },
{ name: 'team_id', type: 'string', isIndexed: true },
{ name: 'team_main', type: 'boolean', isOptional: true }, // Use `Q.notEq(true)` to get false or null
+ { name: 'on_hold', type: 'boolean', isOptional: true },
{ name: 'source', type: 'string', isOptional: true }
]
}),
@@ -117,7 +118,8 @@ export default appSchema({
{ name: 'blocks', type: 'string', isOptional: true },
{ name: 'e2e', type: 'string', isOptional: true },
{ name: 'tshow', type: 'boolean', isOptional: true },
- { name: 'md', type: 'string', isOptional: true }
+ { name: 'md', type: 'string', isOptional: true },
+ { name: 'comment', type: 'string', isOptional: true }
]
}),
tableSchema({
diff --git a/app/lib/methods/subscriptions/rooms.ts b/app/lib/methods/subscriptions/rooms.ts
index 17ab329fe..aa73b09d5 100644
--- a/app/lib/methods/subscriptions/rooms.ts
+++ b/app/lib/methods/subscriptions/rooms.ts
@@ -26,7 +26,8 @@ import {
TMessageModel,
TRoomModel,
TThreadMessageModel,
- TThreadModel
+ TThreadModel,
+ SubscriptionType
} from '../../../definitions';
import sdk from '../../services/sdk';
import { IDDPMessage } from '../../../definitions/IDDPMessage';
@@ -99,7 +100,8 @@ const createOrUpdateSubscription = async (subscription: ISubscription, room: ISe
encrypted: s.encrypted,
e2eKeyId: s.e2eKeyId,
E2EKey: s.E2EKey,
- avatarETag: s.avatarETag
+ avatarETag: s.avatarETag,
+ onHold: s.onHold
} as ISubscription;
} catch (error) {
try {
@@ -251,6 +253,11 @@ const debouncedUpdate = (subscription: ISubscription) => {
createOrUpdateSubscription(sub, room);
} else {
const room = batch[key] as IRoom;
+ // If the omnichannel's chat is onHold and waitingResponse we shouldn't create or update the chat,
+ // because it should go to Queue
+ if (room.t === SubscriptionType.OMNICHANNEL && room.onHold && room.waitingResponse) {
+ return null;
+ }
const subQueueId = getSubQueueId(room._id);
const sub = batch[subQueueId] as ISubscription;
delete batch[subQueueId];
diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts
index d7a140da6..2b18d8706 100644
--- a/app/lib/services/restApi.ts
+++ b/app/lib/services/restApi.ts
@@ -360,6 +360,8 @@ export const returnLivechat = (rid: string): Promise =>
// RC 0.72.0
sdk.methodCallWrapper('livechat:returnAsInquiry', rid);
+export const onHoldLivechat = (roomId: string) => sdk.post('livechat/room.onHold', { roomId });
+
export const forwardLivechat = (transferData: any) =>
// RC 0.36.0
sdk.methodCallWrapper('livechat:transfer', transferData);
diff --git a/app/utils/log/events.ts b/app/utils/log/events.ts
index 82dd3079d..4b9b7a90d 100644
--- a/app/utils/log/events.ts
+++ b/app/utils/log/events.ts
@@ -226,6 +226,7 @@ export default {
ROOM_MSG_ACTION_REPORT: 'room_msg_action_report',
ROOM_MSG_ACTION_REPORT_F: 'room_msg_action_report_f',
ROOM_JOIN: 'room_join',
+ ROOM_RESUME: 'room_resume',
ROOM_GO_RA: 'room_go_ra',
ROOM_TOGGLE_FOLLOW_THREADS: 'room_toggle_follow_threads',
ROOM_GO_TEAM_CHANNELS: 'room_go_team_channels',
diff --git a/app/views/RoomActionsView/index.tsx b/app/views/RoomActionsView/index.tsx
index 45d8b0f52..cdd644864 100644
--- a/app/views/RoomActionsView/index.tsx
+++ b/app/views/RoomActionsView/index.tsx
@@ -63,6 +63,7 @@ interface IRoomActionsViewProps extends IBaseScreen {
@@ -129,13 +132,15 @@ class RoomActionsView extends React.Component {
if (this.mounted) {
- this.setState({ room: changes });
+ this.setState({ room: changes, isOnHold: !!changes?.onHold });
} else {
// @ts-ignore
this.state.room = changes;
@@ -209,11 +214,25 @@ class RoomActionsView extends React.Component {
+ const { livechatAllowManualOnHold } = this.props;
+ const { room } = this.state;
+
+ return !!(livechatAllowManualOnHold && !room?.lastMessage?.token && room?.lastMessage?.u && !room.onHold);
+ };
+
canReturnQueue = async () => {
try {
const { returnQueue } = await RocketChat.getRoutingConfig();
@@ -393,6 +419,24 @@ class RoomActionsView extends React.Component {
+ const { navigation } = this.props;
+ const { room } = this.state;
+ 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 RocketChat.onHoldLivechat(room.rid);
+ navigation.navigate('RoomsListView');
+ } catch (e: any) {
+ showErrorAlert(e.data?.error, I18n.t('Oops'));
+ }
+ }
+ });
+ };
+
returnLivechat = () => {
const {
room: { rid }
@@ -1005,7 +1049,8 @@ class RoomActionsView extends React.Component
) : null}
+ {['l'].includes(t) && !this.isOmnichannelPreview && canPlaceLivechatOnHold ? (
+ <>
+
+ this.onPressTouchable({
+ event: this.placeOnHoldLivechat
+ })
+ }
+ left={() => }
+ showActionIndicator
+ />
+
+ >
+ ) : null}
+
{['l'].includes(t) && !this.isOmnichannelPreview && canReturnQueue ? (
<>
({
createTeamPermission: state.permissions['create-team'],
addTeamChannelPermission: state.permissions['add-team-channel'],
convertTeamPermission: state.permissions['convert-team'],
- viewCannedResponsesPermission: state.permissions['view-canned-responses']
+ viewCannedResponsesPermission: state.permissions['view-canned-responses'],
+ livechatAllowManualOnHold: state.settings.Livechat_allow_manual_on_hold as boolean
});
export default connect(mapStateToProps)(withTheme(withDimensions(RoomActionsView)));
diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx
index 596d64f59..791b09d44 100644
--- a/app/views/RoomView/index.tsx
+++ b/app/views/RoomView/index.tsx
@@ -46,7 +46,7 @@ import Navigation from '../../lib/navigation/appNavigation';
import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions';
import { getHeaderTitlePosition } from '../../containers/Header';
-import { takeInquiry } from '../../ee/omnichannel/lib';
+import { takeInquiry, takeResume } from '../../ee/omnichannel/lib';
import Loading from '../../containers/Loading';
import { goRoom, TGoRoomItem } from '../../utils/goRoom';
import getThreadName from '../../lib/methods/getThreadName';
@@ -116,7 +116,8 @@ const roomAttrsUpdate = [
'visitor',
'joinCodeRequired',
'teamMain',
- 'teamId'
+ 'teamId',
+ 'onHold'
] as const;
interface IRoomViewProps extends IBaseScreen {
@@ -958,6 +959,22 @@ class RoomView extends React.Component {
}
};
+ resumeRoom = async () => {
+ logEvent(events.ROOM_RESUME);
+ try {
+ const { room } = this.state;
+
+ if (this.isOmnichannel) {
+ if ('rid' in room) {
+ await takeResume(room.rid);
+ }
+ this.onJoin();
+ }
+ } catch (e) {
+ log(e);
+ }
+ };
+
getThreadName = (tmid: string, messageId: string) => {
const { rid } = this.state.room;
return getThreadName(rid, tmid, messageId);
@@ -1243,6 +1260,24 @@ class RoomView extends React.Component {
if (!this.rid) {
return null;
}
+ if ('onHold' in room && room.onHold) {
+ return (
+
+
+ {I18n.t('Chat_is_on_hold')}
+
+
+
+ {I18n.t('Resume')}
+
+
+
+ );
+ }
if (!joined && !this.tmid) {
return (
diff --git a/app/views/RoomsListView/index.tsx b/app/views/RoomsListView/index.tsx
index 0dd583495..a1dd33c6d 100644
--- a/app/views/RoomsListView/index.tsx
+++ b/app/views/RoomsListView/index.tsx
@@ -88,7 +88,8 @@ interface IRoomsListViewState {
searching: boolean;
search: ISubscription[];
loading: boolean;
- chatsUpdate: [];
+ chatsUpdate: string[];
+ omnichannelsUpdate: string[];
chats: ISubscription[];
item: ISubscription;
canCreateRoom: boolean;
@@ -107,7 +108,8 @@ const DISCUSSIONS_HEADER = 'Discussions';
const TEAMS_HEADER = 'Teams';
const CHANNELS_HEADER = 'Channels';
const DM_HEADER = 'Direct_Messages';
-const OMNICHANNEL_HEADER = 'Open_Livechats';
+const OMNICHANNEL_HEADER_IN_PROGRESS = 'Open_Livechats';
+const OMNICHANNEL_HEADER_ON_HOLD = 'On_hold_Livechats';
const QUERY_SIZE = 20;
const filterIsUnread = (s: TSubscriptionModel) => (s.unread > 0 || s.tunread?.length > 0 || s.alert) && !s.hideUnreadStatus;
@@ -172,6 +174,7 @@ class RoomsListView extends React.Component nextProps[key] !== this.props[key]);
if (propsUpdated) {
@@ -260,6 +263,12 @@ class RoomsListView extends React.Component {
let tempChats = [] as TSubscriptionModel[];
let chats = data;
+ let omnichannelsUpdate: string[] = [];
let chatsUpdate = [];
if (showUnread) {
/**
@@ -513,8 +522,12 @@ class RoomsListView extends React.Component filterIsOmnichannel(s));
+ const omnichannelInProgress = omnichannel.filter(s => !s.onHold);
+ const omnichannelOnHold = omnichannel.filter(s => s.onHold);
chats = chats.filter(s => !filterIsOmnichannel(s));
- tempChats = this.addRoomsGroup(omnichannel, OMNICHANNEL_HEADER, tempChats);
+ omnichannelsUpdate = omnichannelInProgress.map(s => s.rid);
+ tempChats = this.addRoomsGroup(omnichannelInProgress, OMNICHANNEL_HEADER_IN_PROGRESS, tempChats);
+ tempChats = this.addRoomsGroup(omnichannelOnHold, OMNICHANNEL_HEADER_ON_HOLD, tempChats);
}
// unread
@@ -551,6 +564,7 @@ class RoomsListView extends React.Component