Chore: Migrate ee/omnichannel folder to Typescript (#3749)
* Chore: Migrate ee/omnichannel folder to Typescript * omnichannelstatus and queue list * boolean searching and react.ref * test initi * test and refactor interfaces * minor tweak * minor tweaks
This commit is contained in:
parent
091055a255
commit
b7e523a267
|
@ -4,7 +4,7 @@ import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
import { IAttachment } from './IAttachment';
|
import { IAttachment } from './IAttachment';
|
||||||
import { IMessage } from './IMessage';
|
import { IMessage } from './IMessage';
|
||||||
import { IServedBy } from './IServedBy';
|
import { IServedBy } from './IServedBy';
|
||||||
import { SubscriptionType } from './ISubscription';
|
import { IVisitor, SubscriptionType } from './ISubscription';
|
||||||
import { IUser } from './IUser';
|
import { IUser } from './IUser';
|
||||||
|
|
||||||
interface IRequestTranscript {
|
interface IRequestTranscript {
|
||||||
|
@ -28,11 +28,8 @@ export interface IRoom {
|
||||||
broadcast: boolean;
|
broadcast: boolean;
|
||||||
encrypted: boolean;
|
encrypted: boolean;
|
||||||
ro: boolean;
|
ro: boolean;
|
||||||
v?: {
|
v?: IVisitor;
|
||||||
_id?: string;
|
status?: string;
|
||||||
token?: string;
|
|
||||||
status: 'online' | 'busy' | 'away' | 'offline';
|
|
||||||
};
|
|
||||||
servedBy?: IServedBy;
|
servedBy?: IServedBy;
|
||||||
departmentId?: string;
|
departmentId?: string;
|
||||||
livechatData?: any;
|
livechatData?: any;
|
||||||
|
@ -55,13 +52,11 @@ export enum OmnichannelSourceType {
|
||||||
API = 'api',
|
API = 'api',
|
||||||
OTHER = 'other' // catch-all source type
|
OTHER = 'other' // catch-all source type
|
||||||
}
|
}
|
||||||
export interface IOmnichannelRoom extends Omit<IRoom, 'default' | 'featured' | 'broadcast' | ''> {
|
export interface IOmnichannelRoom extends Partial<Omit<IRoom, 'default' | 'featured' | 'broadcast'>> {
|
||||||
|
_id: string;
|
||||||
|
rid: string;
|
||||||
t: SubscriptionType.OMNICHANNEL;
|
t: SubscriptionType.OMNICHANNEL;
|
||||||
v: {
|
v: IVisitor;
|
||||||
_id?: string;
|
|
||||||
token?: string;
|
|
||||||
status: 'online' | 'busy' | 'away' | 'offline';
|
|
||||||
};
|
|
||||||
email?: {
|
email?: {
|
||||||
// Data used when the room is created from an email, via email Integration.
|
// Data used when the room is created from an email, via email Integration.
|
||||||
inbox: string;
|
inbox: string;
|
||||||
|
@ -83,6 +78,8 @@ export interface IOmnichannelRoom extends Omit<IRoom, 'default' | 'featured' | '
|
||||||
sidebarIcon?: string;
|
sidebarIcon?: string;
|
||||||
// The default sidebar icon
|
// The default sidebar icon
|
||||||
defaultIcon?: string;
|
defaultIcon?: string;
|
||||||
|
_updatedAt?: Date;
|
||||||
|
queuedAt?: Date;
|
||||||
};
|
};
|
||||||
transcriptRequest?: IRequestTranscript;
|
transcriptRequest?: IRequestTranscript;
|
||||||
servedBy?: IServedBy;
|
servedBy?: IServedBy;
|
||||||
|
@ -91,18 +88,22 @@ export interface IOmnichannelRoom extends Omit<IRoom, 'default' | 'featured' | '
|
||||||
|
|
||||||
lastMessage?: IMessage & { token?: string };
|
lastMessage?: IMessage & { token?: string };
|
||||||
|
|
||||||
tags: any;
|
tags?: string[];
|
||||||
closedAt: any;
|
closedAt?: Date;
|
||||||
metrics: any;
|
metrics?: any;
|
||||||
waitingResponse: any;
|
waitingResponse?: any;
|
||||||
responseBy: any;
|
responseBy?: any;
|
||||||
priorityId: any;
|
priorityId?: any;
|
||||||
livechatData: any;
|
livechatData?: any;
|
||||||
queuedAt?: Date;
|
queuedAt?: Date;
|
||||||
|
|
||||||
ts: Date;
|
ts: Date;
|
||||||
label?: string;
|
label?: string;
|
||||||
crmData?: unknown;
|
crmData?: unknown;
|
||||||
|
message?: string;
|
||||||
|
queueOrder?: string;
|
||||||
|
estimatedWaitingTimeQueue?: string;
|
||||||
|
estimatedServiceTimeAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TRoomModel = IRoom & Model;
|
export type TRoomModel = IRoom & Model;
|
||||||
|
|
|
@ -17,11 +17,11 @@ export enum SubscriptionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVisitor {
|
export interface IVisitor {
|
||||||
_id: string;
|
_id?: string;
|
||||||
username: string;
|
token?: string;
|
||||||
token: string;
|
status: 'online' | 'busy' | 'away' | 'offline';
|
||||||
status: string;
|
username?: string;
|
||||||
lastMessageTs: Date;
|
lastMessageTs?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ERoomTypes {
|
export enum ERoomTypes {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// ACTIONS
|
// ACTIONS
|
||||||
|
import { TActionInquiry } from '../../ee/omnichannel/actions/inquiry';
|
||||||
import { TActionActiveUsers } from '../../actions/activeUsers';
|
import { TActionActiveUsers } from '../../actions/activeUsers';
|
||||||
import { TActionApp } from '../../actions/app';
|
import { TActionApp } from '../../actions/app';
|
||||||
import { TActionCreateChannel } from '../../actions/createChannel';
|
import { TActionCreateChannel } from '../../actions/createChannel';
|
||||||
|
@ -30,6 +31,7 @@ import { ISelectedUsers } from '../../reducers/selectedUsers';
|
||||||
import { IServer } from '../../reducers/server';
|
import { IServer } from '../../reducers/server';
|
||||||
import { TSettingsState } from '../../reducers/settings';
|
import { TSettingsState } from '../../reducers/settings';
|
||||||
import { IShare } from '../../reducers/share';
|
import { IShare } from '../../reducers/share';
|
||||||
|
import { IInquiry } from '../../ee/omnichannel/reducers/inquiry';
|
||||||
import { IPermissionsState } from '../../reducers/permissions';
|
import { IPermissionsState } from '../../reducers/permissions';
|
||||||
import { IEnterpriseModules } from '../../reducers/enterpriseModules';
|
import { IEnterpriseModules } from '../../reducers/enterpriseModules';
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ export interface IApplicationState {
|
||||||
usersTyping: any;
|
usersTyping: any;
|
||||||
inviteLinks: IInviteLinks;
|
inviteLinks: IInviteLinks;
|
||||||
createDiscussion: ICreateDiscussion;
|
createDiscussion: ICreateDiscussion;
|
||||||
inquiry: any;
|
inquiry: IInquiry;
|
||||||
enterpriseModules: IEnterpriseModules;
|
enterpriseModules: IEnterpriseModules;
|
||||||
encryption: IEncryption;
|
encryption: IEncryption;
|
||||||
permissions: IPermissionsState;
|
permissions: IPermissionsState;
|
||||||
|
@ -71,5 +73,6 @@ export type TApplicationActions = TActionActiveUsers &
|
||||||
TActionsShare &
|
TActionsShare &
|
||||||
TActionServer &
|
TActionServer &
|
||||||
TActionApp &
|
TActionApp &
|
||||||
|
TActionInquiry &
|
||||||
TActionPermissions &
|
TActionPermissions &
|
||||||
TActionEnterpriseModules;
|
TActionEnterpriseModules;
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import * as types from '../../../actions/actionsTypes';
|
|
||||||
|
|
||||||
export function inquirySetEnabled(enabled) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.SET_ENABLED,
|
|
||||||
enabled
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryReset() {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.RESET
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryQueueAdd(inquiry) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.QUEUE_ADD,
|
|
||||||
inquiry
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryQueueUpdate(inquiry) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.QUEUE_UPDATE,
|
|
||||||
inquiry
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryQueueRemove(inquiryId) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.QUEUE_REMOVE,
|
|
||||||
inquiryId
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryRequest() {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.REQUEST
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquirySuccess(inquiries) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.SUCCESS,
|
|
||||||
inquiries
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inquiryFailure(error) {
|
|
||||||
return {
|
|
||||||
type: types.INQUIRY.FAILURE,
|
|
||||||
error
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IOmnichannelRoom } from '../../../definitions';
|
||||||
|
import { INQUIRY } from '../../../actions/actionsTypes';
|
||||||
|
|
||||||
|
interface IInquirySetEnabled extends Action {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInquiryQueueAddAndUpdate extends Action {
|
||||||
|
inquiry: IOmnichannelRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInquirySuccess extends Action {
|
||||||
|
inquiries: IOmnichannelRoom[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInquiryQueueRemove extends Action {
|
||||||
|
inquiryId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInquiryFailure extends Action {
|
||||||
|
error: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionInquiry = IInquirySetEnabled &
|
||||||
|
IInquiryQueueAddAndUpdate &
|
||||||
|
IInquirySuccess &
|
||||||
|
IInquiryQueueRemove &
|
||||||
|
IInquiryFailure;
|
||||||
|
|
||||||
|
export function inquirySetEnabled(enabled: boolean): IInquirySetEnabled {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.SET_ENABLED,
|
||||||
|
enabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryReset(): Action {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.RESET
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryQueueAdd(inquiry: IOmnichannelRoom): IInquiryQueueAddAndUpdate {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.QUEUE_ADD,
|
||||||
|
inquiry
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryQueueUpdate(inquiry: IOmnichannelRoom): IInquiryQueueAddAndUpdate {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.QUEUE_UPDATE,
|
||||||
|
inquiry
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryQueueRemove(inquiryId: string): IInquiryQueueRemove {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.QUEUE_REMOVE,
|
||||||
|
inquiryId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryRequest(): Action {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.REQUEST
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquirySuccess(inquiries: IOmnichannelRoom[]): IInquirySuccess {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.SUCCESS,
|
||||||
|
inquiries
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inquiryFailure(error: unknown): IInquiryFailure {
|
||||||
|
return {
|
||||||
|
type: INQUIRY.FAILURE,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,19 +1,28 @@
|
||||||
import React, { memo, useEffect, useState } from 'react';
|
import React, { memo, useEffect, useState } from 'react';
|
||||||
import { Switch, View } from 'react-native';
|
import { Switch, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import * as List from '../../../containers/List';
|
import * as List from '../../../containers/List';
|
||||||
import styles from '../../../views/RoomsListView/styles';
|
import styles from '../../../views/RoomsListView/styles';
|
||||||
import { SWITCH_TRACK_COLOR, themes } from '../../../constants/colors';
|
import { SWITCH_TRACK_COLOR, themes } from '../../../constants/colors';
|
||||||
import { withTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import UnreadBadge from '../../../presentation/UnreadBadge';
|
import UnreadBadge from '../../../presentation/UnreadBadge';
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../lib';
|
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../lib';
|
||||||
|
import { IUser } from '../../../definitions/IUser';
|
||||||
|
|
||||||
const OmnichannelStatus = memo(({ searching, goQueue, theme, queueSize, inquiryEnabled, user }) => {
|
interface IOmnichannelStatus {
|
||||||
if (searching > 0 || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
|
searching: boolean;
|
||||||
|
goQueue: () => void;
|
||||||
|
queueSize: number;
|
||||||
|
inquiryEnabled: boolean;
|
||||||
|
user: IUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OmnichannelStatus = memo(({ searching, goQueue, queueSize, inquiryEnabled, user }: IOmnichannelStatus) => {
|
||||||
|
if (searching || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { theme } = useTheme();
|
||||||
const [status, setStatus] = useState(isOmnichannelStatusAvailable(user));
|
const [status, setStatus] = useState(isOmnichannelStatusAvailable(user));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -48,16 +57,4 @@ const OmnichannelStatus = memo(({ searching, goQueue, theme, queueSize, inquiryE
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
OmnichannelStatus.propTypes = {
|
export default OmnichannelStatus;
|
||||||
searching: PropTypes.bool,
|
|
||||||
goQueue: PropTypes.func,
|
|
||||||
queueSize: PropTypes.number,
|
|
||||||
inquiryEnabled: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
user: PropTypes.shape({
|
|
||||||
roles: PropTypes.array,
|
|
||||||
statusLivechat: PropTypes.string
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTheme(OmnichannelStatus);
|
|
|
@ -1,21 +1,24 @@
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import sdk from '../../../lib/rocketchat/services/sdk';
|
||||||
|
import { IUser } from '../../../definitions';
|
||||||
import EventEmitter from '../../../utils/events';
|
import EventEmitter from '../../../utils/events';
|
||||||
import subscribeInquiry from './subscriptions/inquiry';
|
import subscribeInquiry from './subscriptions/inquiry';
|
||||||
|
|
||||||
export const isOmnichannelStatusAvailable = user => user?.statusLivechat === 'available';
|
export const isOmnichannelStatusAvailable = (user: IUser): boolean => user?.statusLivechat === 'available';
|
||||||
|
|
||||||
// RC 0.26.0
|
// RC 0.26.0
|
||||||
export const changeLivechatStatus = () => RocketChat.methodCallWrapper('livechat:changeLivechatStatus');
|
export const changeLivechatStatus = () => sdk.methodCallWrapper('livechat:changeLivechatStatus');
|
||||||
|
|
||||||
// RC 2.4.0
|
// RC 2.4.0
|
||||||
export const getInquiriesQueued = () => RocketChat.sdk.get('livechat/inquiries.queued');
|
// @ts-ignore
|
||||||
|
export const getInquiriesQueued = () => sdk.get('livechat/inquiries.queued');
|
||||||
|
|
||||||
// this inquiry is added to the db by the subscriptions stream
|
// this inquiry is added to the db by the subscriptions stream
|
||||||
// and will be removed by the queue stream
|
// and will be removed by the queue stream
|
||||||
// RC 2.4.0
|
// RC 2.4.0
|
||||||
export const takeInquiry = inquiryId => RocketChat.methodCallWrapper('livechat:takeInquiry', inquiryId);
|
export const takeInquiry = (inquiryId: string) => sdk.methodCallWrapper('livechat:takeInquiry', inquiryId);
|
||||||
|
|
||||||
class Omnichannel {
|
class Omnichannel {
|
||||||
|
private inquirySub: { stop: () => void } | null;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.inquirySub = null;
|
this.inquirySub = null;
|
||||||
EventEmitter.addEventListener('INQUIRY_SUBSCRIBE', this.subscribeInquiry);
|
EventEmitter.addEventListener('INQUIRY_SUBSCRIBE', this.subscribeInquiry);
|
||||||
|
@ -36,5 +39,5 @@ class Omnichannel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const omnichannel = new Omnichannel();
|
const omnichannel = new Omnichannel();
|
|
@ -2,11 +2,28 @@ import log from '../../../../utils/log';
|
||||||
import { store } from '../../../../lib/auxStore';
|
import { store } from '../../../../lib/auxStore';
|
||||||
import RocketChat from '../../../../lib/rocketchat';
|
import RocketChat from '../../../../lib/rocketchat';
|
||||||
import { inquiryQueueAdd, inquiryQueueRemove, inquiryQueueUpdate, inquiryRequest } from '../../actions/inquiry';
|
import { inquiryQueueAdd, inquiryQueueRemove, inquiryQueueUpdate, inquiryRequest } from '../../actions/inquiry';
|
||||||
|
import sdk from '../../../../lib/rocketchat/services/sdk';
|
||||||
|
import { ILivechatDepartment } from '../../../../definitions/ILivechatDepartment';
|
||||||
|
import { IOmnichannelRoom } from '../../../../definitions';
|
||||||
|
|
||||||
const removeListener = listener => listener.stop();
|
interface IArgsQueueOmnichannel extends IOmnichannelRoom {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
let connectedListener;
|
interface IDdpMessage {
|
||||||
let queueListener;
|
msg: string;
|
||||||
|
collection: string;
|
||||||
|
id: string;
|
||||||
|
fields: {
|
||||||
|
eventName: string;
|
||||||
|
args: IArgsQueueOmnichannel[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeListener = (listener: any) => listener.stop();
|
||||||
|
|
||||||
|
let connectedListener: any;
|
||||||
|
let queueListener: any;
|
||||||
|
|
||||||
const streamTopic = 'stream-livechat-inquiry-queue-observer';
|
const streamTopic = 'stream-livechat-inquiry-queue-observer';
|
||||||
|
|
||||||
|
@ -15,7 +32,7 @@ export default function subscribeInquiry() {
|
||||||
store.dispatch(inquiryRequest());
|
store.dispatch(inquiryRequest());
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQueueMessageReceived = ddpMessage => {
|
const handleQueueMessageReceived = (ddpMessage: IDdpMessage) => {
|
||||||
const [{ type, ...sub }] = ddpMessage.fields.args;
|
const [{ type, ...sub }] = ddpMessage.fields.args;
|
||||||
|
|
||||||
// added can be ignored, since it is handled by 'changed' event
|
// added can be ignored, since it is handled by 'changed' event
|
||||||
|
@ -26,7 +43,9 @@ export default function subscribeInquiry() {
|
||||||
// if the sub isn't on the queue anymore
|
// if the sub isn't on the queue anymore
|
||||||
if (sub.status !== 'queued') {
|
if (sub.status !== 'queued') {
|
||||||
// remove it from the queue
|
// remove it from the queue
|
||||||
store.dispatch(inquiryQueueRemove(sub._id));
|
if (sub._id) {
|
||||||
|
store.dispatch(inquiryQueueRemove(sub._id));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,23 +72,28 @@ export default function subscribeInquiry() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
connectedListener = RocketChat.onStreamData('connected', handleConnection);
|
connectedListener = sdk.onStreamData('connected', handleConnection);
|
||||||
queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
|
queueListener = sdk.onStreamData(streamTopic, handleQueueMessageReceived);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { user } = store.getState().login;
|
const { user } = store.getState().login;
|
||||||
RocketChat.getAgentDepartments(user.id).then(result => {
|
|
||||||
|
if (!user.id) {
|
||||||
|
throw new Error('inquiry: @subscribeInquiry user.id not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
RocketChat.getAgentDepartments(user.id).then((result: { success: boolean; departments: ILivechatDepartment[] }) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const { departments } = result;
|
const { departments } = result;
|
||||||
|
|
||||||
if (!departments.length || RocketChat.hasRole('livechat-manager')) {
|
if (!departments.length || RocketChat.hasRole('livechat-manager')) {
|
||||||
RocketChat.subscribe(streamTopic, 'public').catch(e => console.log(e));
|
sdk.subscribe(streamTopic, 'public').catch((e: unknown) => console.log(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
const departmentIds = departments.map(({ departmentId }) => departmentId);
|
const departmentIds = departments.map(({ departmentId }) => departmentId);
|
||||||
departmentIds.forEach(departmentId => {
|
departmentIds.forEach(departmentId => {
|
||||||
// subscribe to all departments of the agent
|
// subscribe to all departments of the agent
|
||||||
RocketChat.subscribe(streamTopic, `department/${departmentId}`).catch(e => console.log(e));
|
sdk.subscribe(streamTopic, `department/${departmentId}`).catch((e: unknown) => console.log(e));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -0,0 +1,98 @@
|
||||||
|
import {
|
||||||
|
inquiryFailure,
|
||||||
|
inquiryQueueAdd,
|
||||||
|
inquiryQueueRemove,
|
||||||
|
inquiryQueueUpdate,
|
||||||
|
inquiryReset,
|
||||||
|
inquirySetEnabled,
|
||||||
|
inquirySuccess
|
||||||
|
} from '../actions/inquiry';
|
||||||
|
import { mockedStore } from '../../../reducers/mockedStore';
|
||||||
|
import { initialState } from './inquiry';
|
||||||
|
import { IOmnichannelRoom, OmnichannelSourceType, SubscriptionType } from '../../../definitions';
|
||||||
|
|
||||||
|
describe('test inquiry reduce', () => {
|
||||||
|
const enabledObj = {
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const queued: IOmnichannelRoom = {
|
||||||
|
_id: '_id',
|
||||||
|
rid: 'rid',
|
||||||
|
name: 'Rocket Chat',
|
||||||
|
ts: new Date(),
|
||||||
|
message: 'ola',
|
||||||
|
status: 'queued',
|
||||||
|
v: {
|
||||||
|
_id: 'id-visitor',
|
||||||
|
username: 'guest-24',
|
||||||
|
token: '123456789',
|
||||||
|
status: 'online'
|
||||||
|
},
|
||||||
|
t: SubscriptionType.OMNICHANNEL,
|
||||||
|
queueOrder: '1',
|
||||||
|
estimatedWaitingTimeQueue: '0',
|
||||||
|
estimatedServiceTimeAt: new Date(),
|
||||||
|
source: {
|
||||||
|
type: OmnichannelSourceType.WIDGET,
|
||||||
|
_updatedAt: new Date(),
|
||||||
|
queuedAt: new Date()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = 'Error Test';
|
||||||
|
|
||||||
|
it('should return inital state', () => {
|
||||||
|
const state = mockedStore.getState().inquiry;
|
||||||
|
expect(state).toEqual(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct inquiry state after dispatch inquirySetEnabled action', () => {
|
||||||
|
mockedStore.dispatch(inquirySetEnabled(enabledObj.enabled));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual({ ...initialState, ...enabledObj });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('after inquiry state is modified, should return inquiry state as initial state after dispatch inquiryReset action', () => {
|
||||||
|
mockedStore.dispatch(inquiryReset());
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct inquiry state after dispatch inquiryQueueAdd action', () => {
|
||||||
|
mockedStore.dispatch(inquiryQueueAdd(queued));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual({ ...initialState, queued: [queued] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update correct inquiry state after dispatch inquiryQueueUpdate action', () => {
|
||||||
|
const modifiedQueued: IOmnichannelRoom = { ...queued, message: 'inquiryQueueUpdate' };
|
||||||
|
mockedStore.dispatch(inquiryQueueUpdate(modifiedQueued));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual({ ...initialState, queued: [modifiedQueued] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove correct from queue in inquiry state after dispatch inquiryQueueRemove action', () => {
|
||||||
|
mockedStore.dispatch(inquiryQueueRemove(queued._id));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct inquiry state after dispatch inquirySuccess action', () => {
|
||||||
|
mockedStore.dispatch(inquirySuccess([queued]));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual({ ...initialState, queued: [queued] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('after inquiry state is modified, should return inquiry state as initial state after dispatch inquiryReset action', () => {
|
||||||
|
mockedStore.dispatch(inquiryReset());
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct inquiry state after dispatch inquiryFailure action', () => {
|
||||||
|
mockedStore.dispatch(inquiryFailure(error));
|
||||||
|
const { inquiry } = mockedStore.getState();
|
||||||
|
expect(inquiry).toEqual({ ...initialState, error });
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,12 +1,19 @@
|
||||||
|
import { IOmnichannelRoom, TApplicationActions } from '../../../definitions';
|
||||||
import { INQUIRY } from '../../../actions/actionsTypes';
|
import { INQUIRY } from '../../../actions/actionsTypes';
|
||||||
|
|
||||||
const initialState = {
|
export interface IInquiry {
|
||||||
|
enabled: boolean;
|
||||||
|
queued: IOmnichannelRoom[];
|
||||||
|
error: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState: IInquiry = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
queued: [],
|
queued: [],
|
||||||
error: {}
|
error: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function inquiry(state = initialState, action) {
|
export default function inquiry(state = initialState, action: TApplicationActions): IInquiry {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case INQUIRY.SUCCESS:
|
case INQUIRY.SUCCESS:
|
||||||
return {
|
return {
|
|
@ -1,5 +1,7 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
const getInquiryQueue = state => state.inquiry.queued;
|
import { IApplicationState } from '../../../definitions';
|
||||||
|
|
||||||
|
const getInquiryQueue = (state: IApplicationState) => state.inquiry.queued;
|
||||||
|
|
||||||
export const getInquiryQueueSelector = createSelector([getInquiryQueue], queue => queue);
|
export const getInquiryQueueSelector = createSelector([getInquiryQueue], queue => queue);
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { CompositeNavigationProp } from '@react-navigation/native';
|
||||||
import { FlatList } from 'react-native';
|
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { FlatList, ListRenderItem } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
|
@ -19,18 +20,50 @@ import * as HeaderButton from '../../../containers/HeaderButton';
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
import { events, logEvent } from '../../../utils/log';
|
import { events, logEvent } from '../../../utils/log';
|
||||||
import { getInquiryQueueSelector } from '../selectors/inquiry';
|
import { getInquiryQueueSelector } from '../selectors/inquiry';
|
||||||
|
import { IOmnichannelRoom, IApplicationState } from '../../../definitions';
|
||||||
|
import { DisplayMode } from '../../../constants/constantDisplayMode';
|
||||||
|
import { ChatsStackParamList } from '../../../stacks/types';
|
||||||
|
import { MasterDetailInsideStackParamList } from '../../../stacks/MasterDetailStack/types';
|
||||||
|
import { TSettingsValues } from '../../../reducers/settings';
|
||||||
|
|
||||||
|
interface INavigationOptions {
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
navigation: CompositeNavigationProp<
|
||||||
|
StackNavigationProp<ChatsStackParamList, 'QueueListView'>,
|
||||||
|
StackNavigationProp<MasterDetailInsideStackParamList>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IQueueListView extends INavigationOptions {
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
width: number;
|
||||||
|
queued: IOmnichannelRoom[];
|
||||||
|
server: string;
|
||||||
|
useRealName?: TSettingsValues;
|
||||||
|
theme: string;
|
||||||
|
showAvatar: any;
|
||||||
|
displayMode: DisplayMode;
|
||||||
|
}
|
||||||
|
|
||||||
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
|
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
|
||||||
const getItemLayout = (data, index) => ({
|
const getItemLayout = (data: IOmnichannelRoom[] | null | undefined, index: number) => ({
|
||||||
length: ROW_HEIGHT,
|
length: ROW_HEIGHT,
|
||||||
offset: ROW_HEIGHT * index,
|
offset: ROW_HEIGHT * index,
|
||||||
index
|
index
|
||||||
});
|
});
|
||||||
const keyExtractor = item => item.rid;
|
const keyExtractor = (item: IOmnichannelRoom) => item.rid;
|
||||||
|
|
||||||
class QueueListView extends React.Component {
|
class QueueListView extends React.Component<IQueueListView, any> {
|
||||||
static navigationOptions = ({ navigation, isMasterDetail }) => {
|
private getScrollRef?: React.Ref<FlatList<IOmnichannelRoom>>;
|
||||||
const options = {
|
|
||||||
|
private onEndReached: ((info: { distanceFromEnd: number }) => void) | null | undefined;
|
||||||
|
|
||||||
|
static navigationOptions = ({ navigation, isMasterDetail }: INavigationOptions) => {
|
||||||
|
const options: StackNavigationOptions = {
|
||||||
title: I18n.t('Queued_chats')
|
title: I18n.t('Queued_chats')
|
||||||
};
|
};
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
|
@ -39,24 +72,7 @@ class QueueListView extends React.Component {
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
shouldComponentUpdate(nextProps: IQueueListView) {
|
||||||
user: PropTypes.shape({
|
|
||||||
id: PropTypes.string,
|
|
||||||
username: PropTypes.string,
|
|
||||||
token: PropTypes.string
|
|
||||||
}),
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
width: PropTypes.number,
|
|
||||||
queued: PropTypes.array,
|
|
||||||
server: PropTypes.string,
|
|
||||||
useRealName: PropTypes.bool,
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
showAvatar: PropTypes.bool,
|
|
||||||
displayMode: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
|
||||||
const { queued } = this.props;
|
const { queued } = this.props;
|
||||||
if (!dequal(nextProps.queued, queued)) {
|
if (!dequal(nextProps.queued, queued)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -65,7 +81,7 @@ class QueueListView extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressItem = (item = {}) => {
|
onPressItem = (item = {} as IOmnichannelRoom) => {
|
||||||
logEvent(events.QL_GO_ROOM);
|
logEvent(events.QL_GO_ROOM);
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
|
@ -84,13 +100,13 @@ class QueueListView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getRoomTitle = item => RocketChat.getRoomTitle(item);
|
getRoomTitle = (item: IOmnichannelRoom) => RocketChat.getRoomTitle(item);
|
||||||
|
|
||||||
getRoomAvatar = item => RocketChat.getRoomAvatar(item);
|
getRoomAvatar = (item: IOmnichannelRoom) => RocketChat.getRoomAvatar(item);
|
||||||
|
|
||||||
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
|
getUidDirectMessage = (room: IOmnichannelRoom) => RocketChat.getUidDirectMessage(room);
|
||||||
|
|
||||||
renderItem = ({ item }) => {
|
renderItem: ListRenderItem<IOmnichannelRoom> = ({ item }) => {
|
||||||
const {
|
const {
|
||||||
user: { id: userId, username, token },
|
user: { id: userId, username, token },
|
||||||
server,
|
server,
|
||||||
|
@ -152,7 +168,7 @@ class QueueListView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
server: state.server.server,
|
server: state.server.server,
|
|
@ -1,7 +1,7 @@
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { ISubscription, IVisitor, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription';
|
import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../definitions';
|
||||||
|
|
||||||
interface IGoRoomItem {
|
interface IGoRoomItem {
|
||||||
search?: boolean; // comes from spotlight
|
search?: boolean; // comes from spotlight
|
||||||
|
@ -13,7 +13,7 @@ interface IGoRoomItem {
|
||||||
visitor?: IVisitor;
|
visitor?: IVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription;
|
export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription | IOmnichannelRoomVisitor;
|
||||||
|
|
||||||
const navigate = ({
|
const navigate = ({
|
||||||
item,
|
item,
|
||||||
|
@ -42,6 +42,11 @@ const navigate = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface IOmnichannelRoomVisitor extends IOmnichannelRoom {
|
||||||
|
// this visitor came from ee/omnichannel/views/QueueListView
|
||||||
|
visitor: IVisitor;
|
||||||
|
}
|
||||||
|
|
||||||
export const goRoom = async ({
|
export const goRoom = async ({
|
||||||
item,
|
item,
|
||||||
isMasterDetail = false,
|
isMasterDetail = false,
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
export interface ILivechatDepartment {
|
|
||||||
_id: string;
|
|
||||||
name: string;
|
|
||||||
enabled: boolean;
|
|
||||||
description: string;
|
|
||||||
showOnRegistration: boolean;
|
|
||||||
showOnOfflineForm: boolean;
|
|
||||||
requestTagBeforeClosingChat: boolean;
|
|
||||||
email: string;
|
|
||||||
chatClosingTags: string[];
|
|
||||||
offlineMessageChannelName: string;
|
|
||||||
numAgents: number;
|
|
||||||
_updatedAt?: Date;
|
|
||||||
businessHourId?: string;
|
|
||||||
}
|
|
Loading…
Reference in New Issue